/**
 * Muuri v0.7.1
 * https://github.com/haltu/muuri
 * Copyright (c) 2015-present, Haltu Oy
 * Released under the MIT license
 * https://github.com/haltu/muuri/blob/master/LICENSE.md
 * @license MIT
 *
 * Muuri Packer
 * Copyright (c) 2016-present, Niklas Rämö <inramo@gmail.com>
 * @license MIT
 *
 * Muuri Ticker / Muuri Emitter / Muuri Queue
 * Copyright (c) 2018-present, Niklas Rämö <inramo@gmail.com>
 * @license MIT
 */

(function (global, factory) {
	if (typeof exports === 'object' && typeof module !== 'undefined') {
	  var Hammer;
	  try { Hammer = require('hammerjs') } catch (e) {}
	  module.exports = factory(Hammer);
	} else {
	  global.Muuri = factory(global.Hammer);
	}
  }(this, (function (Hammer) {
	'use strict';
  
	var namespace = 'Muuri';
	var gridInstances = {};
  
	var eventSynchronize = 'synchronize';
	var eventLayoutStart = 'layoutStart';
	var eventLayoutEnd = 'layoutEnd';
	var eventAdd = 'add';
	var eventRemove = 'remove';
	var eventShowStart = 'showStart';
	var eventShowEnd = 'showEnd';
	var eventHideStart = 'hideStart';
	var eventHideEnd = 'hideEnd';
	var eventFilter = 'filter';
	var eventSort = 'sort';
	var eventMove = 'move';
	var eventSend = 'send';
	var eventBeforeSend = 'beforeSend';
	var eventReceive = 'receive';
	var eventBeforeReceive = 'beforeReceive';
	var eventDragInit = 'dragInit';
	var eventDragStart = 'dragStart';
	var eventDragMove = 'dragMove';
	var eventDragScroll = 'dragScroll';
	var eventDragEnd = 'dragEnd';
	var eventDragReleaseStart = 'dragReleaseStart';
	var eventDragReleaseEnd = 'dragReleaseEnd';
	var eventDestroy = 'destroy';
  
	/**
	 * Event emitter constructor.
	 *
	 * @class
	 */
	function Emitter() {
	  this._events = {};
	  this._queue = [];
	  this._counter = 0;
	  this._isDestroyed = false;
	}
  
	/**
	 * Public prototype methods
	 * ************************
	 */
  
	/**
	 * Bind an event listener.
	 *
	 * @public
	 * @memberof Emitter.prototype
	 * @param {String} event
	 * @param {Function} listener
	 * @returns {Emitter}
	 */
	Emitter.prototype.on = function(event, listener) {
	  if (this._isDestroyed) return this;
  
	  // Get listeners queue and create it if it does not exist.
	  var listeners = this._events[event];
	  if (!listeners) listeners = this._events[event] = [];
  
	  // Add the listener to the queue.
	  listeners.push(listener);
  
	  return this;
	};
  
	/**
	 * Bind an event listener that is triggered only once.
	 *
	 * @public
	 * @memberof Emitter.prototype
	 * @param {String} event
	 * @param {Function} listener
	 * @returns {Emitter}
	 */
	Emitter.prototype.once = function(event, listener) {
	  if (this._isDestroyed) return this;
  
	  var callback = function() {
		this.off(event, callback);
		listener.apply(null, arguments);
	  }.bind(this);
  
	  return this.on(event, callback);
	};
  
	/**
	 * Unbind all event listeners that match the provided listener function.
	 *
	 * @public
	 * @memberof Emitter.prototype
	 * @param {String} event
	 * @param {Function} [listener]
	 * @returns {Emitter}
	 */
	Emitter.prototype.off = function(event, listener) {
	  if (this._isDestroyed) return this;
  
	  // Get listeners and return immediately if none is found.
	  var listeners = this._events[event];
	  if (!listeners || !listeners.length) return this;
  
	  // If no specific listener is provided remove all listeners.
	  if (!listener) {
		listeners.length = 0;
		return this;
	  }
  
	  // Remove all matching listeners.
	  var i = listeners.length;
	  while (i--) {
		if (listener === listeners[i]) listeners.splice(i, 1);
	  }
  
	  return this;
	};
  
	/**
	 * Emit all listeners in a specified event with the provided arguments.
	 *
	 * @public
	 * @memberof Emitter.prototype
	 * @param {String} event
	 * @param {*} [arg1]
	 * @param {*} [arg2]
	 * @param {*} [arg3]
	 * @returns {Emitter}
	 */
	Emitter.prototype.emit = function(event, arg1, arg2, arg3) {
	  if (this._isDestroyed) return this;
  
	  // Get event listeners and quit early if there's no listeners.
	  var listeners = this._events[event];
	  if (!listeners || !listeners.length) return this;
  
	  var queue = this._queue;
	  var qLength = queue.length;
	  var aLength = arguments.length - 1;
	  var i;
  
	  // Add the current listeners to the callback queue before we process them.
	  // This is necessary to guarantee that all of the listeners are called in
	  // correct order even if new event listeners are removed/added during
	  // processing and/or events are emitted during processing.
	  for (i = 0; i < listeners.length; i++) {
		queue.push(listeners[i]);
	  }
  
	  // Increment queue counter. This is needed for the scenarios where emit is
	  // triggered while the queue is already processing. We need to keep track of
	  // how many "queue processors" there are active so that we can safely reset
	  // the queue in the end when the last queue processor is finished.
	  ++this._counter;
  
	  // Process the queue (the specific part of it for this emit).
	  for (i = qLength, qLength = queue.length; i < qLength; i++) {
		// prettier-ignore
		aLength === 0 ? queue[i]() :
		aLength === 1 ? queue[i](arg1) :
		aLength === 2 ? queue[i](arg1, arg2) :
						queue[i](arg1, arg2, arg3);
  
		// Stop processing if the emitter is destroyed.
		if (this._isDestroyed) return this;
	  }
  
	  // Decrement queue process counter.
	  --this._counter;
  
	  // Reset the queue if there are no more queue processes running.
	  if (!this._counter) queue.length = 0;
  
	  return this;
	};
  
	/**
	 * Destroy emitter instance. Basically just removes all bound listeners.
	 *
	 * @public
	 * @memberof Emitter.prototype
	 * @returns {Emitter}
	 */
	Emitter.prototype.destroy = function() {
	  if (this._isDestroyed) return this;
  
	  var events = this._events;
	  var event;
  
	  // Flag as destroyed.
	  this._isDestroyed = true;
  
	  // Reset queue (if queue is currently processing this will also stop that).
	  this._queue.length = this._counter = 0;
  
	  // Remove all listeners.
	  for (event in events) {
		if (events[event]) {
		  events[event].length = 0;
		  events[event] = undefined;
		}
	  }
  
	  return this;
	};
  
	// Set up the default export values.
	var isTransformSupported = false;
	var transformStyle = 'transform';
	var transformProp = 'transform';
  
	// Find the supported transform prop and style names.
	var style = 'transform';
	var styleCap = 'Transform';
	['', 'Webkit', 'Moz', 'O', 'ms'].forEach(function(prefix) {
	  if (isTransformSupported) return;
	  var propName = prefix ? prefix + styleCap : style;
	  if (document.documentElement.style[propName] !== undefined) {
		prefix = prefix.toLowerCase();
		transformStyle = prefix ? '-' + prefix + '-' + style : style;
		transformProp = propName;
		isTransformSupported = true;
	  }
	});
  
	var stylesCache = typeof WeakMap === 'function' ? new WeakMap() : null;
  
	/**
	 * Returns the computed value of an element's style property as a string.
	 *
	 * @param {HTMLElement} element
	 * @param {String} style
	 * @returns {String}
	 */
	function getStyle(element, style) {
	  var styles = stylesCache && stylesCache.get(element);
	  if (!styles) {
		styles = window.getComputedStyle(element, null);
		stylesCache && stylesCache.set(element, styles);
	  }
	  return styles.getPropertyValue(style === 'transform' ? transformStyle : style);
	}
  
	var styleNameRegEx = /([A-Z])/g;
  
	/**
	 * Transforms a camel case style property to kebab case style property.
	 *
	 * @param {String} string
	 * @returns {String}
	 */
	function getStyleName(string) {
	  return string.replace(styleNameRegEx, '-$1').toLowerCase();
	}
  
	/**
	 * Set inline styles to an element.
	 *
	 * @param {HTMLElement} element
	 * @param {Object} styles
	 */
	function setStyles(element, styles) {
	  for (var prop in styles) {
		element.style[prop === 'transform' ? transformProp : prop] = styles[prop];
	  }
	}
  
	/**
	 * Item animation handler powered by Web Animations API.
	 *
	 * @class
	 * @param {HTMLElement} element
	 */
	function ItemAnimate(element) {
	  this._element = element;
	  this._animation = null;
	  this._callback = null;
	  this._props = [];
	  this._values = [];
	  this._keyframes = [];
	  this._options = {};
	  this._isDestroyed = false;
	  this._onFinish = this._onFinish.bind(this);
	}
  
	/**
	 * Public prototype methods
	 * ************************
	 */
  
	/**
	 * Start instance's animation. Automatically stops current animation if it is
	 * running.
	 *
	 * @public
	 * @memberof ItemAnimate.prototype
	 * @param {Object} propsFrom
	 * @param {Object} propsTo
	 * @param {Object} [options]
	 * @param {Number} [options.duration=300]
	 * @param {String} [options.easing='ease']
	 * @param {Function} [options.onFinish]
	 */
	ItemAnimate.prototype.start = function(propsFrom, propsTo, options) {
	  if (this._isDestroyed) return;
  
	  var animation = this._animation;
	  var currentProps = this._props;
	  var currentValues = this._values;
	  var opts = options || 0;
	  var cancelAnimation = false;
  
	  // If we have an existing animation running, let's check if it needs to be
	  // cancelled or if it can continue running.
	  if (animation) {
		var propCount = 0;
		var propIndex;
  
		// Check if the requested animation target props and values match with the
		// current props and values.
		for (var propName in propsTo) {
		  ++propCount;
		  propIndex = currentProps.indexOf(propName);
		  if (propIndex === -1 || propsTo[propName] !== currentValues[propIndex]) {
			cancelAnimation = true;
			break;
		  }
		}
  
		// Check if the target props count matches current props count. This is
		// needed for the edge case scenario where target props contain the same
		// styles as current props, but the current props have some additional
		// props.
		if (!cancelAnimation && propCount !== currentProps.length) {
		  cancelAnimation = true;
		}
	  }
  
	  // Cancel animation (if required).
	  if (cancelAnimation) animation.cancel();
  
	  // Store animation callback.
	  this._callback = typeof opts.onFinish === 'function' ? opts.onFinish : null;
  
	  // If we have a running animation that does not need to be cancelled, let's
	  // call it a day here and let it run.
	  if (animation && !cancelAnimation) return;
  
	  // Store target props and values to instance.
	  currentProps.length = currentValues.length = 0;
	  for (propName in propsTo) {
		currentProps.push(propName);
		currentValues.push(propsTo[propName]);
	  }
  
	  // Set up keyframes.
	  var animKeyframes = this._keyframes;
	  animKeyframes[0] = propsFrom;
	  animKeyframes[1] = propsTo;
  
	  // Set up options.
	  var animOptions = this._options;
	  animOptions.duration = opts.duration || 300;
	  animOptions.easing = opts.easing || 'ease';
  
	  // Start the animation
	  var element = this._element;
	  animation = element.animate(animKeyframes, animOptions);
	  animation.onfinish = this._onFinish;
	  this._animation = animation;
  
	  // Set the end styles. This makes sure that the element stays at the end
	  // values after animation is finished.
	  setStyles(element, propsTo);
	};
  
	/**
	 * Stop instance's current animation if running.
	 *
	 * @public
	 * @memberof ItemAnimate.prototype
	 * @param {Object} [styles]
	 */
	ItemAnimate.prototype.stop = function(styles) {
	  if (this._isDestroyed || !this._animation) return;
  
	  var element = this._element;
	  var currentProps = this._props;
	  var currentValues = this._values;
	  var propName;
	  var propValue;
	  var i;
  
	  // Calculate (if not provided) and set styles.
	  if (!styles) {
		for (i = 0; i < currentProps.length; i++) {
		  propName = currentProps[i];
		  propValue = getStyle(element, getStyleName(propName));
		  element.style[propName === 'transform' ? transformProp : propName] = propValue;
		}
	  } else {
		setStyles(element, styles);
	  }
  
	  //  Cancel animation.
	  this._animation.cancel();
	  this._animation = this._callback = null;
  
	  // Reset current props and values.
	  currentProps.length = currentValues.length = 0;
	};
  
	/**
	 * Check if the item is being animated currently.
	 *
	 * @public
	 * @memberof ItemAnimate.prototype
	 * @return {Boolean}
	 */
	ItemAnimate.prototype.isAnimating = function() {
	  return !!this._animation;
	};
  
	/**
	 * Destroy the instance and stop current animation if it is running.
	 *
	 * @public
	 * @memberof ItemAnimate.prototype
	 */
	ItemAnimate.prototype.destroy = function() {
	  if (this._isDestroyed) return;
	  this.stop();
	  this._element = this._options = this._keyframes = null;
	  this._isDestroyed = true;
	};
  
	/**
	 * Private prototype methods
	 * *************************
	 */
  
	/**
	 * Animation end handler.
	 *
	 * @private
	 * @memberof ItemAnimate.prototype
	 */
	ItemAnimate.prototype._onFinish = function() {
	  var callback = this._callback;
	  this._animation = this._callback = null;
	  this._props.length = this._values.length = 0;
	  callback && callback();
	};
  
	var raf = (
	  window.requestAnimationFrame ||
	  window.webkitRequestAnimationFrame ||
	  window.mozRequestAnimationFrame ||
	  window.msRequestAnimationFrame ||
	  rafFallback
	).bind(window);
  
	function rafFallback(cb) {
	  return window.setTimeout(cb, 16);
	}
  
	/**
	 * A ticker system for handling DOM reads and writes in an efficient way.
	 * Contains a read queue and a write queue that are processed on the next
	 * animation frame when needed.
	 *
	 * @class
	 */
	function Ticker() {
	  this._nextTick = null;
  
	  this._queue = [];
	  this._reads = {};
	  this._writes = {};
  
	  this._batch = [];
	  this._batchReads = {};
	  this._batchWrites = {};
  
	  this._flush = this._flush.bind(this);
	}
  
	Ticker.prototype.add = function(id, readCallback, writeCallback, isImportant) {
	  // First, let's check if an item has been added to the queues with the same id
	  // and if so -> remove it.
	  var currentIndex = this._queue.indexOf(id);
	  if (currentIndex > -1) this._queue[currentIndex] = undefined;
  
	  // Add all important callbacks to the beginning of the queue and other
	  // callbacks to the end of the queue.
	  isImportant ? this._queue.unshift(id) : this._queue.push(id);
  
	  // Store callbacks.
	  this._reads[id] = readCallback;
	  this._writes[id] = writeCallback;
  
	  // Finally, let's kick-start the next tick if it is not running yet.
	  if (!this._nextTick) this._nextTick = raf(this._flush);
	};
  
	Ticker.prototype.cancel = function(id) {
	  var currentIndex = this._queue.indexOf(id);
	  if (currentIndex > -1) {
		this._queue[currentIndex] = undefined;
		this._reads[id] = undefined;
		this._writes[id] = undefined;
	  }
	};
  
	Ticker.prototype._flush = function() {
	  var queue = this._queue;
	  var reads = this._reads;
	  var writes = this._writes;
	  var batch = this._batch;
	  var batchReads = this._batchReads;
	  var batchWrites = this._batchWrites;
	  var length = queue.length;
	  var id;
	  var i;
  
	  // Reset ticker.
	  this._nextTick = null;
  
	  // Setup queues and callback placeholders.
	  for (i = 0; i < length; i++) {
		id = queue[i];
		if (!id) continue;
  
		batch.push(id);
  
		batchReads[id] = reads[id];
		reads[id] = undefined;
  
		batchWrites[id] = writes[id];
		writes[id] = undefined;
	  }
  
	  // Reset queue.
	  queue.length = 0;
  
	  // Process read callbacks.
	  for (i = 0; i < length; i++) {
		id = batch[i];
		if (batchReads[id]) {
		  batchReads[id]();
		  batchReads[id] = undefined;
		}
	  }
  
	  // Process write callbacks.
	  for (i = 0; i < length; i++) {
		id = batch[i];
		if (batchWrites[id]) {
		  batchWrites[id]();
		  batchWrites[id] = undefined;
		}
	  }
  
	  // Reset batch.
	  batch.length = 0;
  
	  // Restart the ticker if needed.
	  if (!this._nextTick && queue.length) {
		this._nextTick = raf(this._flush);
	  }
	};
  
	var ticker = new Ticker();
  
	var layoutTick = 'layout';
	var visibilityTick = 'visibility';
	var moveTick = 'move';
	var scrollTick = 'scroll';
  
	function addLayoutTick(itemId, readCallback, writeCallback) {
	  return ticker.add(itemId + layoutTick, readCallback, writeCallback);
	}
  
	function cancelLayoutTick(itemId) {
	  return ticker.cancel(itemId + layoutTick);
	}
  
	function addVisibilityTick(itemId, readCallback, writeCallback) {
	  return ticker.add(itemId + visibilityTick, readCallback, writeCallback);
	}
  
	function cancelVisibilityTick(itemId) {
	  return ticker.cancel(itemId + visibilityTick);
	}
  
	function addMoveTick(itemId, readCallback, writeCallback) {
	  return ticker.add(itemId + moveTick, readCallback, writeCallback, true);
	}
  
	function cancelMoveTick(itemId) {
	  return ticker.cancel(itemId + moveTick);
	}
  
	function addScrollTick(itemId, readCallback, writeCallback) {
	  return ticker.add(itemId + scrollTick, readCallback, writeCallback, true);
	}
  
	function cancelScrollTick(itemId) {
	  return ticker.cancel(itemId + scrollTick);
	}
  
	var proto = Element.prototype;
	var matches =
	  proto.matches ||
	  proto.matchesSelector ||
	  proto.webkitMatchesSelector ||
	  proto.mozMatchesSelector ||
	  proto.msMatchesSelector ||
	  proto.oMatchesSelector;
  
	/**
	 * Check if element matches a CSS selector.
	 *
	 * @param {*} val
	 * @returns {Boolean}
	 */
	function elementMatches(el, selector) {
	  return matches.call(el, selector);
	}
  
	/**
	 * Add class to an element.
	 *
	 * @param {HTMLElement} element
	 * @param {String} className
	 */
	function addClassModern(element, className) {
	  element.classList.add(className);
	}
  
	/**
	 * Add class to an element (legacy version, for IE9 support).
	 *
	 * @param {HTMLElement} element
	 * @param {String} className
	 */
	function addClassLegacy(element, className) {
	  if (!elementMatches(element, '.' + className)) {
		element.className += ' ' + className;
	  }
	}
  
	var addClass = ('classList' in Element.prototype ? addClassModern : addClassLegacy);
  
	/**
	 * Normalize array index. Basically this function makes sure that the provided
	 * array index is within the bounds of the provided array and also transforms
	 * negative index to the matching positive index.
	 *
	 * @param {Array} array
	 * @param {Number} index
	 * @param {Boolean} isMigration
	 */
	function normalizeArrayIndex(array, index, isMigration) {
	  var length = array.length;
	  var maxIndex = Math.max(0, isMigration ? length : length - 1);
	  return index > maxIndex ? maxIndex : index < 0 ? Math.max(maxIndex + index + 1, 0) : index;
	}
  
	/**
	 * Move array item to another index.
	 *
	 * @param {Array} array
	 * @param {Number} fromIndex
	 *   - Index (positive or negative) of the item that will be moved.
	 * @param {Number} toIndex
	 *   - Index (positive or negative) where the item should be moved to.
	 */
	function arrayMove(array, fromIndex, toIndex) {
	  // Make sure the array has two or more items.
	  if (array.length < 2) return;
  
	  // Normalize the indices.
	  var from = normalizeArrayIndex(array, fromIndex);
	  var to = normalizeArrayIndex(array, toIndex);
  
	  // Add target item to the new position.
	  if (from !== to) {
		array.splice(to, 0, array.splice(from, 1)[0]);
	  }
	}
  
	/**
	 * Swap array items.
	 *
	 * @param {Array} array
	 * @param {Number} index
	 *   - Index (positive or negative) of the item that will be swapped.
	 * @param {Number} withIndex
	 *   - Index (positive or negative) of the other item that will be swapped.
	 */
	function arraySwap(array, index, withIndex) {
	  // Make sure the array has two or more items.
	  if (array.length < 2) return;
  
	  // Normalize the indices.
	  var indexA = normalizeArrayIndex(array, index);
	  var indexB = normalizeArrayIndex(array, withIndex);
	  var temp;
  
	  // Swap the items.
	  if (indexA !== indexB) {
		temp = array[indexA];
		array[indexA] = array[indexB];
		array[indexB] = temp;
	  }
	}
  
	var actionCancel = 'cancel';
	var actionFinish = 'finish';
  
	/**
	 * Returns a function, that, as long as it continues to be invoked, will not
	 * be triggered. The function will be called after it stops being called for
	 * N milliseconds. The returned function accepts one argument which, when
	 * being "finish", calls the debounce function immediately if it is currently
	 * waiting to be called, and when being "cancel" cancels the currently queued
	 * function call.
	 *
	 * @param {Function} fn
	 * @param {Number} wait
	 * @returns {Function}
	 */
	function debounce(fn, wait) {
	  var timeout;
  
	  if (wait > 0) {
		return function(action) {
		  if (timeout !== undefined) {
			timeout = window.clearTimeout(timeout);
			if (action === actionFinish) fn();
		  }
  
		  if (action !== actionCancel && action !== actionFinish) {
			timeout = window.setTimeout(function() {
			  timeout = undefined;
			  fn();
			}, wait);
		  }
		};
	  }
  
	  return function(action) {
		if (action !== actionCancel) fn();
	  };
	}
  
	/**
	 * Returns true if element is transformed, false if not. In practice the
	 * element's display value must be anything else than "none" or "inline" as
	 * well as have a valid transform value applied in order to be counted as a
	 * transformed element.
	 *
	 * Borrowed from Mezr (v0.6.1):
	 * https://github.com/niklasramo/mezr/blob/0.6.1/mezr.js#L661
	 *
	 * @param {HTMLElement} element
	 * @returns {Boolean}
	 */
	function isTransformed(element) {
	  var transform = getStyle(element, 'transform');
	  if (!transform || transform === 'none') return false;
  
	  var display = getStyle(element, 'display');
	  if (display === 'inline' || display === 'none') return false;
  
	  return true;
	}
  
	/**
	 * Returns an absolute positioned element's containing block, which is
	 * considered to be the closest ancestor element that the target element's
	 * positioning is relative to. Disclaimer: this only works as intended for
	 * absolute positioned elements.
	 *
	 * @param {HTMLElement} element
	 * @param {Boolean} [includeSelf=false]
	 *   - When this is set to true the containing block checking is started from
	 *     the provided element. Otherwise the checking is started from the
	 *     provided element's parent element.
	 * @returns {(Document|Element)}
	 */
	function getContainingBlock(element, includeSelf) {
	  // As long as the containing block is an element, static and not
	  // transformed, try to get the element's parent element and fallback to
	  // document. https://github.com/niklasramo/mezr/blob/0.6.1/mezr.js#L339
	  var ret = (includeSelf ? element : element.parentElement) || document;
	  while (ret && ret !== document && getStyle(ret, 'position') === 'static' && !isTransformed(ret)) {
		ret = ret.parentElement || document;
	  }
	  return ret;
	}
  
	/**
	 * Returns the computed value of an element's style property transformed into
	 * a float value.
	 *
	 * @param {HTMLElement} el
	 * @param {String} style
	 * @returns {Number}
	 */
	function getStyleAsFloat(el, style) {
	  return parseFloat(getStyle(el, style)) || 0;
	}
  
	var offsetA = {};
	var offsetB = {};
	var offsetDiff = {};
  
	/**
	 * Returns the element's document offset, which in practice means the vertical
	 * and horizontal distance between the element's northwest corner and the
	 * document's northwest corner. Note that this function always returns the same
	 * object so be sure to read the data from it instead using it as a reference.
	 *
	 * @param {(Document|Element|Window)} element
	 * @param {Object} [offsetData]
	 *   - Optional data object where the offset data will be inserted to. If not
	 *     provided a new object will be created for the return data.
	 * @returns {Object}
	 */
	function getOffset(element, offsetData) {
	  var ret = offsetData || {};
	  var rect;
  
	  // Set up return data.
	  ret.left = 0;
	  ret.top = 0;
  
	  // Document's offsets are always 0.
	  if (element === document) return ret;
  
	  // Add viewport scroll left/top to the respective offsets.
	  ret.left = window.pageXOffset || 0;
	  ret.top = window.pageYOffset || 0;
  
	  // Window's offsets are the viewport scroll left/top values.
	  if (element.self === window.self) return ret;
  
	  // Add element's client rects to the offsets.
	  rect = element.getBoundingClientRect();
	  ret.left += rect.left;
	  ret.top += rect.top;
  
	  // Exclude element's borders from the offset.
	  ret.left += getStyleAsFloat(element, 'border-left-width');
	  ret.top += getStyleAsFloat(element, 'border-top-width');
  
	  return ret;
	}
  
	/**
	 * Calculate the offset difference two elements.
	 *
	 * @param {HTMLElement} elemA
	 * @param {HTMLElement} elemB
	 * @param {Boolean} [compareContainingBlocks=false]
	 *   - When this is set to true the containing blocks of the provided elements
	 *     will be used for calculating the difference. Otherwise the provided
	 *     elements will be compared directly.
	 * @returns {Object}
	 */
	function getOffsetDiff(elemA, elemB, compareContainingBlocks) {
	  offsetDiff.left = 0;
	  offsetDiff.top = 0;
  
	  // If elements are same let's return early.
	  if (elemA === elemB) return offsetDiff;
  
	  // Compare containing blocks if necessary.
	  if (compareContainingBlocks) {
		elemA = getContainingBlock(elemA, true);
		elemB = getContainingBlock(elemB, true);
  
		// If containing blocks are identical, let's return early.
		if (elemA === elemB) return offsetDiff;
	  }
  
	  // Finally, let's calculate the offset diff.
	  getOffset(elemA, offsetA);
	  getOffset(elemB, offsetB);
	  offsetDiff.left = offsetB.left - offsetA.left;
	  offsetDiff.top = offsetB.top - offsetA.top;
  
	  return offsetDiff;
	}
  
	var translateData = {};
  
	/**
	 * Returns the element's computed translateX and translateY values as a floats.
	 * The returned object is always the same object and updated every time this
	 * function is called.
	 *
	 * @param {HTMLElement} element
	 * @returns {Object}
	 */
	function getTranslate(element) {
	  translateData.x = 0;
	  translateData.y = 0;
  
	  var transform = getStyle(element, 'transform');
	  if (!transform) return translateData;
  
	  var matrixData = transform.replace('matrix(', '').split(',');
	  translateData.x = parseFloat(matrixData[4]) || 0;
	  translateData.y = parseFloat(matrixData[5]) || 0;
  
	  return translateData;
	}
  
	/**
	 * Transform translateX and translateY value into CSS transform style
	 * property's value.
	 *
	 * @param {Number} x
	 * @param {Number} y
	 * @returns {String}
	 */
	function getTranslateString(x, y) {
	  return 'translateX(' + x + 'px) translateY(' + y + 'px)';
	}
  
	var tempArray = [];
  
	/**
	 * Insert an item or an array of items to array to a specified index. Mutates
	 * the array. The index can be negative in which case the items will be added
	 * to the end of the array.
	 *
	 * @param {Array} array
	 * @param {*} items
	 * @param {Number} [index=-1]
	 */
	function arrayInsert(array, items, index) {
	  var startIndex = typeof index === 'number' ? index : -1;
	  if (startIndex < 0) startIndex = array.length - startIndex + 1;
  
	  array.splice.apply(array, tempArray.concat(startIndex, 0, items));
	  tempArray.length = 0;
	}
  
	var objectType = '[object Object]';
	var toString = Object.prototype.toString;
  
	/**
	 * Check if a value is a plain object.
	 *
	 * @param {*} val
	 * @returns {Boolean}
	 */
	function isPlainObject(val) {
	  return typeof val === 'object' && toString.call(val) === objectType;
	}
  
	/**
	 * Remove class from an element.
	 *
	 * @param {HTMLElement} element
	 * @param {String} className
	 */
	function removeClassModern(element, className) {
	  element.classList.remove(className);
	}
  
	/**
	 * Remove class from an element (legacy version, for IE9 support).
	 *
	 * @param {HTMLElement} element
	 * @param {String} className
	 */
	function removeClassLegacy(element, className) {
	  if (elementMatches(element, '.' + className)) {
		element.className = (' ' + element.className + ' ').replace(' ' + className + ' ', ' ').trim();
	  }
	}
  
	var removeClass = ('classList' in Element.prototype ? removeClassModern : removeClassLegacy);
  
	// To provide consistently correct dragging experience we need to know if
	// transformed elements leak fixed elements or not.
	var hasTransformLeak = checkTransformLeak();
  
	// Drag start predicate states.
	var startPredicateInactive = 0;
	var startPredicatePending = 1;
	var startPredicateResolved = 2;
	var startPredicateRejected = 3;
  
	/**
	 * Bind Hammer touch interaction to an item.
	 *
	 * @class
	 * @param {Item} item
	 */
	function ItemDrag(item) {
	  if (!Hammer) {
		throw new Error('[' + namespace + '] required dependency Hammer is not defined.');
	  }
  
	  // If we don't have a valid transform leak test result yet, let's run the
	  // test on first ItemDrag init. The test needs body element to be ready and
	  // here we can be sure that it is ready.
	  if (hasTransformLeak === null) {
		hasTransformLeak = checkTransformLeak();
	  }
  
	  var drag = this;
	  var element = item._element;
	  var grid = item.getGrid();
	  var settings = grid._settings;
	  var hammer;
  
	  // Start predicate private data.
	  var startPredicate =
		typeof settings.dragStartPredicate === 'function'
		  ? settings.dragStartPredicate
		  : ItemDrag.defaultStartPredicate;
	  var startPredicateState = startPredicateInactive;
	  var startPredicateResult;
  
	  // Protected data.
	  this._item = item;
	  this._gridId = grid._id;
	  this._hammer = hammer = new Hammer.Manager(element);
	  this._isDestroyed = false;
	  this._isMigrating = false;
  
	  // Setup item's initial drag data.
	  this._reset();
  
	  // Bind some methods that needs binding.
	  this._onScroll = this._onScroll.bind(this);
	  this._prepareMove = this._prepareMove.bind(this);
	  this._applyMove = this._applyMove.bind(this);
	  this._prepareScroll = this._prepareScroll.bind(this);
	  this._applyScroll = this._applyScroll.bind(this);
	  this._checkOverlap = this._checkOverlap.bind(this);
  
	  // Create a private drag start resolver that can be used to resolve the drag
	  // start predicate asynchronously.
	  this._forceResolveStartPredicate = function(event) {
		if (!this._isDestroyed && startPredicateState === startPredicatePending) {
		  startPredicateState = startPredicateResolved;
		  this._onStart(event);
		}
	  };
  
	  // Create debounce overlap checker function.
	  this._checkOverlapDebounce = debounce(this._checkOverlap, settings.dragSortInterval);
  
	  // Add drag recognizer to hammer.
	  hammer.add(
		new Hammer.Pan({
		  event: 'drag',
		  pointers: 1,
		  threshold: 0,
		  direction: Hammer.DIRECTION_ALL
		})
	  );
  
	  // Add drag init recognizer to hammer.
	  hammer.add(
		new Hammer.Press({
		  event: 'draginit',
		  pointers: 1,
		  threshold: 1000,
		  time: 0
		})
	  );
  
	  // Configure the hammer instance.
	  if (isPlainObject(settings.dragHammerSettings)) {
		hammer.set(settings.dragHammerSettings);
	  }
		var isMove=false;
	  // Bind drag events.
	  hammer.on("dragstart",function(e){
			// console.log('click',e)
			try {
				var is=false;
				e.srcEvent.path.forEach(ele=>{
					if(ele.className && ele.className.indexOf("dragmove")!=-1){
						is=true
					}
				})
				if(!is) 
					isMove=true
				else
					isMove=false
			} catch (error) {
				console.error(error)
				isMove=false
			}
		})
		.on('draginit dragstart dragmove', function(e) {
			// console.log(e)
			if(isMove) return
		  // Let's activate drag start predicate state.
		  if (startPredicateState === startPredicateInactive) {
			startPredicateState = startPredicatePending;
		  }
  
		  // If predicate is pending try to resolve it.
		  if (startPredicateState === startPredicatePending) {
			startPredicateResult = startPredicate(drag._item, e);
			if (startPredicateResult === true) {
			  startPredicateState = startPredicateResolved;
			  drag._onStart(e);
			} else if (startPredicateResult === false) {
			  startPredicateState = startPredicateRejected;
			}
		  }
  
		  // Otherwise if predicate is resolved and drag is active, move the item.
		  else if (startPredicateState === startPredicateResolved && drag._isActive) {
			drag._onMove(e);
		  }
		})
		.on('dragend dragcancel draginitup', function(e) {
		  // Check if the start predicate was resolved during drag.
		  var isResolved = startPredicateState === startPredicateResolved;
  
		  // Do final predicate check to allow user to unbind stuff for the current
		  // drag procedure within the predicate callback. The return value of this
		  // check will have no effect to the state of the predicate.
		  startPredicate(drag._item, e);
  
		  // Reset start predicate state.
		  startPredicateState = startPredicateInactive;
  
		  // If predicate is resolved and dragging is active, call the end handler.
		  if (isResolved && drag._isActive) drag._onEnd(e);
		});
  
	  // Prevent native link/image dragging for the item and it's ancestors.
	  element.addEventListener('dragstart', preventDefault, false);
	}
  
	/**
	 * Public static methods
	 * *********************
	 */
  
	/**
	 * Default drag start predicate handler that handles anchor elements
	 * gracefully. The return value of this function defines if the drag is
	 * started, rejected or pending. When true is returned the dragging is started
	 * and when false is returned the dragging is rejected. If nothing is returned
	 * the predicate will be called again on the next drag movement.
	 *
	 * @public
	 * @memberof ItemDrag
	 * @param {Item} item
	 * @param {Object} event
	 * @param {Object} [options]
	 *   - An optional options object which can be used to pass the predicate
	 *     it's options manually. By default the predicate retrieves the options
	 *     from the grid's settings.
	 * @returns {Boolean}
	 */
	ItemDrag.defaultStartPredicate = function(item, event, options) {
	  var drag = item._drag;
	  var predicate = drag._startPredicateData || drag._setupStartPredicate(options);
  
	  // Final event logic. At this stage return value does not matter anymore,
	  // the predicate is either resolved or it's not and there's nothing to do
	  // about it. Here we just reset data and if the item element is a link
	  // we follow it (if there has only been slight movement).
	  if (event.isFinal) {
		drag._finishStartPredicate(event);
		return;
	  }
  
	  // Find and store the handle element so we can check later on if the
	  // cursor is within the handle. If we have a handle selector let's find
	  // the corresponding element. Otherwise let's use the item element as the
	  // handle.
	  if (!predicate.handleElement) {
		predicate.handleElement = drag._getStartPredicateHandle(event);
		if (!predicate.handleElement) return false;
	  }
  
	  // If delay is defined let's keep track of the latest event and initiate
	  // delay if it has not been done yet.
	  if (predicate.delay) {
		predicate.event = event;
		if (!predicate.delayTimer) {
		  predicate.delayTimer = window.setTimeout(function() {
			predicate.delay = 0;
			if (drag._resolveStartPredicate(predicate.event)) {
			  drag._forceResolveStartPredicate(predicate.event);
			  drag._resetStartPredicate();
			}
		  }, predicate.delay);
		}
	  }
  
	  return drag._resolveStartPredicate(event);
	};
  
	/**
	 * Default drag sort predicate.
	 *
	 * @public
	 * @memberof ItemDrag
	 * @param {Item} item
	 * @param {Object} [options]
	 * @param {Number} [options.threshold=50]
	 * @param {String} [options.action='move']
	 * @returns {(Boolean|DragSortCommand)}
	 *   - Returns false if no valid index was found. Otherwise returns drag sort
	 *     command.
	 */
	ItemDrag.defaultSortPredicate = (function() {
	  var itemRect = {};
	  var targetRect = {};
	  var returnData = {};
	  var rootGridArray = [];
  
	  function getTargetGrid(item, rootGrid, threshold) {
		var target = null;
		var dragSort = rootGrid._settings.dragSort;
		var bestScore = -1;
		var gridScore;
		var grids;
		var grid;
		var i;
  
		// Get potential target grids.
		if (dragSort === true) {
		  rootGridArray[0] = rootGrid;
		  grids = rootGridArray;
		} else {
		  grids = dragSort.call(rootGrid, item);
		}
  
		// Return immediately if there are no grids.
		if (!Array.isArray(grids)) return target;
  
		// Loop through the grids and get the best match.
		for (i = 0; i < grids.length; i++) {
		  grid = grids[i];
  
		  // Filter out all destroyed grids.
		  if (grid._isDestroyed) continue;
  
		  // We need to update the grid's offsets and dimensions since they might
		  // have changed (e.g during scrolling).
		  grid._updateBoundingRect();
  
		  // Check how much dragged element overlaps the container element.
		  targetRect.width = grid._width;
		  targetRect.height = grid._height;
		  targetRect.left = grid._left;
		  targetRect.top = grid._top;
		  gridScore = getRectOverlapScore(itemRect, targetRect);
  
		  // Check if this grid is the best match so far.
		  if (gridScore > threshold && gridScore > bestScore) {
			bestScore = gridScore;
			target = grid;
		  }
		}
  
		// Always reset root grid array.
		rootGridArray.length = 0;
  
		return target;
	  }
  
	  return function(item, options) {
		var drag = item._drag;
		var rootGrid = drag._getGrid();
  
		// Get drag sort predicate settings.
		var sortThreshold = options && typeof options.threshold === 'number' ? options.threshold : 50;
		var sortAction = options && options.action === 'swap' ? 'swap' : 'move';
  
		// Populate item rect data.
		itemRect.width = item._width;
		itemRect.height = item._height;
		itemRect.left = drag._elementClientX;
		itemRect.top = drag._elementClientY;
  
		// Calculate the target grid.
		var grid = getTargetGrid(item, rootGrid, sortThreshold);
  
		// Return early if we found no grid container element that overlaps the
		// dragged item enough.
		if (!grid) return false;
  
		var gridOffsetLeft = 0;
		var gridOffsetTop = 0;
		var matchScore = -1;
		var matchIndex;
		var hasValidTargets;
		var target;
		var score;
		var i;
  
		// If item is moved within it's originating grid adjust item's left and
		// top props. Otherwise if item is moved to/within another grid get the
		// container element's offset (from the element's content edge).
		if (grid === rootGrid) {
		  itemRect.left = drag._gridX + item._marginLeft;
		  itemRect.top = drag._gridY + item._marginTop;
		} else {
		  grid._updateBorders(1, 0, 1, 0);
		  gridOffsetLeft = grid._left + grid._borderLeft;
		  gridOffsetTop = grid._top + grid._borderTop;
		}
  
		// Loop through the target grid items and try to find the best match.
		for (i = 0; i < grid._items.length; i++) {
		  target = grid._items[i];
  
		  // If the target item is not active or the target item is the dragged
		  // item let's skip to the next item.
		  if (!target._isActive || target === item) {
			continue;
		  }
  
		  // Mark the grid as having valid target items.
		  hasValidTargets = true;
  
		  // Calculate the target's overlap score with the dragged item.
		  targetRect.width = target._width;
		  targetRect.height = target._height;
		  targetRect.left = target._left + target._marginLeft + gridOffsetLeft;
		  targetRect.top = target._top + target._marginTop + gridOffsetTop;
		  score = getRectOverlapScore(itemRect, targetRect);
  
		  // Update best match index and score if the target's overlap score with
		  // the dragged item is higher than the current best match score.
		  if (score > matchScore) {
			matchIndex = i;
			matchScore = score;
		  }
		}
  
		// If there is no valid match and the item is being moved into another
		// grid.
		if (matchScore < sortThreshold && item.getGrid() !== grid) {
		  matchIndex = hasValidTargets ? -1 : 0;
		  matchScore = Infinity;
		}
  
		// Check if the best match overlaps enough to justify a placement switch.
		if (matchScore >= sortThreshold) {
		  returnData.grid = grid;
		  returnData.index = matchIndex;
		  returnData.action = sortAction;
		  return returnData;
		}
  
		return false;
	  };
	})();
  
	/**
	 * Public prototype methods
	 * ************************
	 */
  
	/**
	 * Abort dragging and reset drag data.
	 *
	 * @public
	 * @memberof ItemDrag.prototype
	 * @returns {ItemDrag}
	 */
	ItemDrag.prototype.stop = function() {
	  var item = this._item;
	  var element = item._element;
	  var grid = this._getGrid();
  
	  if (!this._isActive) return this;
  
	  // If the item is being dropped into another grid, finish it up and return
	  // immediately.
	  if (this._isMigrating) {
		this._finishMigration();
		return this;
	  }
  
	  // Cancel queued move and scroll ticks.
	  cancelMoveTick(item._id);
	  cancelScrollTick(item._id);
  
	  // Remove scroll listeners.
	  this._unbindScrollListeners();
  
	  // Cancel overlap check.
	  this._checkOverlapDebounce('cancel');
  
	  // Append item element to the container if it's not it's child. Also make
	  // sure the translate values are adjusted to account for the DOM shift.
	  if (element.parentNode !== grid._element) {
		grid._element.appendChild(element);
		element.style[transformProp] = getTranslateString(this._gridX, this._gridY);
	  }
  
	  // Remove dragging class.
	  removeClass(element, grid._settings.itemDraggingClass);
  
	  // Reset drag data.
	  this._reset();
  
	  return this;
	};
  
	/**
	 * Destroy instance.
	 *
	 * @public
	 * @memberof ItemDrag.prototype
	 * @returns {ItemDrag}
	 */
	ItemDrag.prototype.destroy = function() {
	  if (this._isDestroyed) return this;
	  this.stop();
	  this._hammer.destroy();
	  this._item._element.removeEventListener('dragstart', preventDefault, false);
	  this._isDestroyed = true;
	  return this;
	};
  
	/**
	 * Private prototype methods
	 * *************************
	 */
  
	/**
	 * Get Grid instance.
	 *
	 * @private
	 * @memberof ItemDrag.prototype
	 * @returns {?Grid}
	 */
	ItemDrag.prototype._getGrid = function() {
	  return gridInstances[this._gridId] || null;
	};
  
	/**
	 * Setup/reset drag data.
	 *
	 * @private
	 * @memberof ItemDrag.prototype
	 */
	ItemDrag.prototype._reset = function() {
	  // Is item being dragged?
	  this._isActive = false;
  
	  // The dragged item's container element.
	  this._container = null;
  
	  // The dragged item's containing block.
	  this._containingBlock = null;
  
	  // Hammer event data.
	  this._lastEvent = null;
	  this._lastScrollEvent = null;
  
	  // All the elements which need to be listened for scroll events during
	  // dragging.
	  this._scrollers = [];
  
	  // The current translateX/translateY position.
	  this._left = 0;
	  this._top = 0;
  
	  // Dragged element's current position within the grid.
	  this._gridX = 0;
	  this._gridY = 0;
  
	  // Dragged element's current offset from window's northwest corner. Does
	  // not account for element's margins.
	  this._elementClientX = 0;
	  this._elementClientY = 0;
  
	  // Offset difference between the dragged element's temporary drag
	  // container and it's original container.
	  this._containerDiffX = 0;
	  this._containerDiffY = 0;
	};
  
	/**
	 * Bind drag scroll handlers to all scrollable ancestor elements of the
	 * dragged element and the drag container element.
	 *
	 * @private
	 * @memberof ItemDrag.prototype
	 */
	ItemDrag.prototype._bindScrollListeners = function() {
	  var gridContainer = this._getGrid()._element;
	  var dragContainer = this._container;
	  var scrollers = this._scrollers;
	  var containerScrollers;
	  var i;
  
	  // Get dragged element's scrolling parents.
	  scrollers.length = 0;
	  getScrollParents(this._item._element, scrollers);
  
	  // If drag container is defined and it's not the same element as grid
	  // container then we need to add the grid container and it's scroll parents
	  // to the elements which are going to be listener for scroll events.
	  if (dragContainer !== gridContainer) {
		containerScrollers = [];
		getScrollParents(gridContainer, containerScrollers);
		containerScrollers.push(gridContainer);
		for (i = 0; i < containerScrollers.length; i++) {
		  if (scrollers.indexOf(containerScrollers[i]) < 0) {
			scrollers.push(containerScrollers[i]);
		  }
		}
	  }
  
	  // Bind scroll listeners.
	  for (i = 0; i < scrollers.length; i++) {
		scrollers[i].addEventListener('scroll', this._onScroll);
	  }
	};
  
	/**
	 * Unbind currently bound drag scroll handlers from all scrollable ancestor
	 * elements of the dragged element and the drag container element.
	 *
	 * @private
	 * @memberof ItemDrag.prototype
	 */
	ItemDrag.prototype._unbindScrollListeners = function() {
	  var scrollers = this._scrollers;
	  var i;
  
	  for (i = 0; i < scrollers.length; i++) {
		scrollers[i].removeEventListener('scroll', this._onScroll);
	  }
  
	  scrollers.length = 0;
	};
  
	/**
	 * Setup default start predicate.
	 *
	 * @private
	 * @memberof ItemDrag.prototype
	 * @param {Object} [options]
	 * @returns {Object}
	 */
	ItemDrag.prototype._setupStartPredicate = function(options) {
	  var config = options || this._getGrid()._settings.dragStartPredicate || 0;
	  return (this._startPredicateData = {
		distance: Math.abs(config.distance) || 0,
		delay: Math.max(config.delay, 0) || 0,
		handle: typeof config.handle === 'string' ? config.handle : false
	  });
	};
  
	/**
	 * Setup default start predicate handle.
	 *
	 * @private
	 * @memberof ItemDrag.prototype
	 * @param {Object} event
	 * @returns {?HTMLElement}
	 */
	ItemDrag.prototype._getStartPredicateHandle = function(event) {
	  var predicate = this._startPredicateData;
	  var element = this._item._element;
	  var handleElement = element;
  
	  // No handle, no hassle -> let's use the item element as the handle.
	  if (!predicate.handle) return handleElement;
  
	  // If there is a specific predicate handle defined, let's try to get it.
	  handleElement = (event.changedPointers[0] || 0).target;
	  while (handleElement && !elementMatches(handleElement, predicate.handle)) {
		handleElement = handleElement !== element ? handleElement.parentElement : null;
	  }
	  return handleElement || null;
	};
  
	/**
	 * Unbind currently bound drag scroll handlers from all scrollable ancestor
	 * elements of the dragged element and the drag container element.
	 *
	 * @private
	 * @memberof ItemDrag.prototype
	 * @param {Object} event
	 * @returns {Boolean}
	 */
	ItemDrag.prototype._resolveStartPredicate = function(event) {
	  var predicate = this._startPredicateData;
	  var pointer = event.changedPointers[0];
	  var pageX = (pointer && pointer.pageX) || 0;
	  var pageY = (pointer && pointer.pageY) || 0;
	  var handleRect;
	  var handleLeft;
	  var handleTop;
	  var handleWidth;
	  var handleHeight;
  
	  // If the moved distance is smaller than the threshold distance or there is
	  // some delay left, ignore this predicate cycle.
	  if (event.distance < predicate.distance || predicate.delay) return;
  
	  // Get handle rect data.
	  handleRect = predicate.handleElement.getBoundingClientRect();
	  handleLeft = handleRect.left + (window.pageXOffset || 0);
	  handleTop = handleRect.top + (window.pageYOffset || 0);
	  handleWidth = handleRect.width;
	  handleHeight = handleRect.height;
  
	  // Reset predicate data.
	  this._resetStartPredicate();
  
	  // If the cursor is still within the handle let's start the drag.
	  return (
		handleWidth &&
		handleHeight &&
		pageX >= handleLeft &&
		pageX < handleLeft + handleWidth &&
		pageY >= handleTop &&
		pageY < handleTop + handleHeight
	  );
	};
  
	/**
	 * Finalize start predicate.
	 *
	 * @private
	 * @memberof ItemDrag.prototype
	 * @param {Object} event
	 */
	ItemDrag.prototype._finishStartPredicate = function(event) {
	  var element = this._item._element;
  
	  // Reset predicate.
	  this._resetStartPredicate();
  
	  // If the gesture can be interpreted as click let's try to open the element's
	  // href url (if it is an anchor element).
	  if (isClick(event)) openAnchorHref(element);
	};
  
	/**
	 * Reset for default drag start predicate function.
	 *
	 * @private
	 * @memberof ItemDrag.prototype
	 */
	ItemDrag.prototype._resetStartPredicate = function() {
	  var predicate = this._startPredicateData;
	  if (predicate) {
		if (predicate.delayTimer) {
		  predicate.delayTimer = window.clearTimeout(predicate.delayTimer);
		}
		this._startPredicateData = null;
	  }
	};
  
	/**
	 * Check (during drag) if an item is overlapping other items and based on
	 * the configuration layout the items.
	 *
	 * @private
	 * @memberof ItemDrag.prototype
	 */
	ItemDrag.prototype._checkOverlap = function() {
	  if (!this._isActive) return;
  
	  var item = this._item;
	  var settings = this._getGrid()._settings;
	  var result;
	  var currentGrid;
	  var currentIndex;
	  var targetGrid;
	  var targetIndex;
	  var sortAction;
	  var isMigration;
  
	  // Get overlap check result.
	  if (typeof settings.dragSortPredicate === 'function') {
		result = settings.dragSortPredicate(item, this._lastEvent);
	  } else {
		result = ItemDrag.defaultSortPredicate(item, settings.dragSortPredicate);
	  }
  
	  // Let's make sure the result object has a valid index before going further.
	  if (!result || typeof result.index !== 'number') return;
  
	  currentGrid = item.getGrid();
	  targetGrid = result.grid || currentGrid;
	  isMigration = currentGrid !== targetGrid;
	  currentIndex = currentGrid._items.indexOf(item);
	  targetIndex = normalizeArrayIndex(targetGrid._items, result.index, isMigration);
	  sortAction = result.action === 'swap' ? 'swap' : 'move';
  
	  // If the item was moved within it's current grid.
	  if (!isMigration) {
		// Make sure the target index is not the current index.
		if (currentIndex !== targetIndex) {
		  // Do the sort.
		  (sortAction === 'swap' ? arraySwap : arrayMove)(
			currentGrid._items,
			currentIndex,
			targetIndex
		  );
  
		  // Emit move event.
		  if (currentGrid._hasListeners(eventMove)) {
			currentGrid._emit(eventMove, {
			  item: item,
			  fromIndex: currentIndex,
			  toIndex: targetIndex,
			  action: sortAction
			});
		  }
  
		  // Layout the grid.
		  currentGrid.layout();
		}
	  }
  
	  // If the item was moved to another grid.
	  else {
		// Emit beforeSend event.
		if (currentGrid._hasListeners(eventBeforeSend)) {
		  currentGrid._emit(eventBeforeSend, {
			item: item,
			fromGrid: currentGrid,
			fromIndex: currentIndex,
			toGrid: targetGrid,
			toIndex: targetIndex
		  });
		}
  
		// Emit beforeReceive event.
		if (targetGrid._hasListeners(eventBeforeReceive)) {
		  targetGrid._emit(eventBeforeReceive, {
			item: item,
			fromGrid: currentGrid,
			fromIndex: currentIndex,
			toGrid: targetGrid,
			toIndex: targetIndex
		  });
		}
  
		// Update item's grid id reference.
		item._gridId = targetGrid._id;
  
		// Update drag instance's migrating indicator.
		this._isMigrating = item._gridId !== this._gridId;
  
		// Move item instance from current grid to target grid.
		currentGrid._items.splice(currentIndex, 1);
		arrayInsert(targetGrid._items, item, targetIndex);
  
		// Set sort data as null, which is an indicator for the item comparison
		// function that the sort data of this specific item should be fetched
		// lazily.
		item._sortData = null;
  
		// Emit send event.
		if (currentGrid._hasListeners(eventSend)) {
		  currentGrid._emit(eventSend, {
			item: item,
			fromGrid: currentGrid,
			fromIndex: currentIndex,
			toGrid: targetGrid,
			toIndex: targetIndex
		  });
		}
  
		// Emit receive event.
		if (targetGrid._hasListeners(eventReceive)) {
		  targetGrid._emit(eventReceive, {
			item: item,
			fromGrid: currentGrid,
			fromIndex: currentIndex,
			toGrid: targetGrid,
			toIndex: targetIndex
		  });
		}
  
		// Layout both grids.
		currentGrid.layout();
		targetGrid.layout();
	  }
	};
  
	/**
	 * If item is dragged into another grid, finish the migration process
	 * gracefully.
	 *
	 * @private
	 * @memberof ItemDrag.prototype
	 */
	ItemDrag.prototype._finishMigration = function() {
	  var item = this._item;
	  var release = item._release;
	  var element = item._element;
	  var isActive = item._isActive;
	  var targetGrid = item.getGrid();
	  var targetGridElement = targetGrid._element;
	  var targetSettings = targetGrid._settings;
	  var targetContainer = targetSettings.dragContainer || targetGridElement;
	  var currentSettings = this._getGrid()._settings;
	  var currentContainer = element.parentNode;
	  var translate;
	  var offsetDiff;
  
	  // Destroy current drag. Note that we need to set the migrating flag to
	  // false first, because otherwise we create an infinite loop between this
	  // and the drag.stop() method.
	  this._isMigrating = false;
	  this.destroy();
  
	  // Remove current classnames.
	  removeClass(element, currentSettings.itemClass);
	  removeClass(element, currentSettings.itemVisibleClass);
	  removeClass(element, currentSettings.itemHiddenClass);
  
	  // Add new classnames.
	  addClass(element, targetSettings.itemClass);
	  addClass(element, isActive ? targetSettings.itemVisibleClass : targetSettings.itemHiddenClass);
  
	  // Move the item inside the target container if it's different than the
	  // current container.
	  if (targetContainer !== currentContainer) {
		targetContainer.appendChild(element);
		offsetDiff = getOffsetDiff(currentContainer, targetContainer, true);
		translate = getTranslate(element);
		translate.x -= offsetDiff.left;
		translate.y -= offsetDiff.top;
	  }
  
	  // Update item's cached dimensions and sort data.
	  item._refreshDimensions();
	  item._refreshSortData();
  
	  // Calculate the offset difference between target's drag container (if any)
	  // and actual grid container element. We save it later for the release
	  // process.
	  offsetDiff = getOffsetDiff(targetContainer, targetGridElement, true);
	  release._containerDiffX = offsetDiff.left;
	  release._containerDiffY = offsetDiff.top;
  
	  // Recreate item's drag handler.
	  item._drag = targetSettings.dragEnabled ? new ItemDrag(item) : null;
  
	  // Adjust the position of the item element if it was moved from a container
	  // to another.
	  if (targetContainer !== currentContainer) {
		element.style[transformProp] = getTranslateString(translate.x, translate.y);
	  }
  
	  // Update child element's styles to reflect the current visibility state.
	  item._child.removeAttribute('style');
	  setStyles(item._child, isActive ? targetSettings.visibleStyles : targetSettings.hiddenStyles);
  
	  // Start the release.
	  release.start();
	};
  
	/**
	 * Drag start handler.
	 *
	 * @private
	 * @memberof ItemDrag.prototype
	 * @param {Object} event
	 */
	ItemDrag.prototype._onStart = function(event) {
	  var item = this._item;
  
	  // If item is not active, don't start the drag.
	  if (!item._isActive) return;
  
	  var element = item._element;
	  var grid = this._getGrid();
	  var settings = grid._settings;
	  var release = item._release;
	  var migrate = item._migrate;
	  var gridContainer = grid._element;
	  var dragContainer = settings.dragContainer || gridContainer;
	  var containingBlock = getContainingBlock(dragContainer, true);
	  var translate = getTranslate(element);
	  var currentLeft = translate.x;
	  var currentTop = translate.y;
	  var elementRect = element.getBoundingClientRect();
	  var hasDragContainer = dragContainer !== gridContainer;
	  var offsetDiff;
  
	  // If grid container is not the drag container, we need to calculate the
	  // offset difference between grid container and drag container's containing
	  // element.
	  if (hasDragContainer) {
		offsetDiff = getOffsetDiff(containingBlock, gridContainer);
	  }
  
	  // Stop current positioning animation.
	  if (item.isPositioning()) {
		item._layout.stop(true, { transform: getTranslateString(currentLeft, currentTop) });
	  }
  
	  // Stop current migration animation.
	  if (migrate._isActive) {
		currentLeft -= migrate._containerDiffX;
		currentTop -= migrate._containerDiffY;
		migrate.stop(true, { transform: getTranslateString(currentLeft, currentTop) });
	  }
  
	  // If item is being released reset release data.
	  if (item.isReleasing()) release._reset();
  
	  // Setup drag data.
	  this._isActive = true;
	  this._lastEvent = event;
	  this._container = dragContainer;
	  this._containingBlock = containingBlock;
	  this._elementClientX = elementRect.left;
	  this._elementClientY = elementRect.top;
	  this._left = this._gridX = currentLeft;
	  this._top = this._gridY = currentTop;
  
	  // Emit dragInit event.
	  grid._emit(eventDragInit, item, event);
  
	  // If a specific drag container is set and it is different from the
	  // grid's container element we need to cast some extra spells.
	  if (hasDragContainer) {
		// Store the container offset diffs to drag data.
		this._containerDiffX = offsetDiff.left;
		this._containerDiffY = offsetDiff.top;
  
		// If the dragged element is a child of the drag container all we need to
		// do is setup the relative drag position data.
		if (element.parentNode === dragContainer) {
		  this._gridX = currentLeft - this._containerDiffX;
		  this._gridY = currentTop - this._containerDiffY;
		}
  
		// Otherwise we need to append the element inside the correct container,
		// setup the actual drag position data and adjust the element's translate
		// values to account for the DOM position shift.
		else {
		  this._left = currentLeft + this._containerDiffX;
		  this._top = currentTop + this._containerDiffY;
		  dragContainer.appendChild(element);
		  element.style[transformProp] = getTranslateString(this._left, this._top);
		}
	  }
  
	  // Set drag class and bind scrollers.
	  addClass(element, settings.itemDraggingClass);
	  this._bindScrollListeners();
  
	  // Emit dragStart event.
	  grid._emit(eventDragStart, item, event);
	};
  
	/**
	 * Drag move handler.
	 *
	 * @private
	 * @memberof ItemDrag.prototype
	 * @param {Object} event
	 */
	ItemDrag.prototype._onMove = function(event) {
	  var item = this._item;
  
	  // If item is not active, reset drag.
	  if (!item._isActive) {
		this.stop();
		return;
	  }
  
	  var settings = this._getGrid()._settings;
	  var axis = settings.dragAxis;
	  var xDiff = event.deltaX - this._lastEvent.deltaX;
	  var yDiff = event.deltaY - this._lastEvent.deltaY;
  
	  // Update last event.
	  this._lastEvent = event;
  
	  // Update horizontal position data.
	  if (axis !== 'y') {
		this._left += xDiff;
		this._gridX += xDiff;
		this._elementClientX += xDiff;
	  }
  
	  // Update vertical position data.
	  if (axis !== 'x') {
		this._top += yDiff;
		this._gridY += yDiff;
		this._elementClientY += yDiff;
	  }
  
	  // Do move prepare/apply handling in the next tick.
	  addMoveTick(item._id, this._prepareMove, this._applyMove);
	};
  
	/**
	 * Prepare dragged item for moving.
	 *
	 * @private
	 * @memberof ItemDrag.prototype
	 */
	ItemDrag.prototype._prepareMove = function() {
	  // Do nothing if item is not active.
	  if (!this._item._isActive) return;
  
	  // If drag sort is enabled -> check overlap.
	  if (this._getGrid()._settings.dragSort) this._checkOverlapDebounce();
	};
  
	/**
	 * Apply movement to dragged item.
	 *
	 * @private
	 * @memberof ItemDrag.prototype
	 */
	ItemDrag.prototype._applyMove = function() {
	  var item = this._item;
  
	  // Do nothing if item is not active.
	  if (!item._isActive) return;
  
	  // Update element's translateX/Y values.
	  item._element.style[transformProp] = getTranslateString(this._left, this._top);
  
	  // Emit dragMove event.
	  this._getGrid()._emit(eventDragMove, item, this._lastEvent);
	};
  
	/**
	 * Drag scroll handler.
	 *
	 * @private
	 * @memberof ItemDrag.prototype
	 * @param {Object} event
	 */
	ItemDrag.prototype._onScroll = function(event) {
	  var item = this._item;
  
	  // If item is not active, reset drag.
	  if (!item._isActive) {
		this.stop();
		return;
	  }
  
	  // Update last scroll event.
	  this._lastScrollEvent = event;
  
	  // Do scroll prepare/apply handling in the next tick.
	  addScrollTick(item._id, this._prepareScroll, this._applyScroll);
	};
  
	/**
	 * Prepare dragged item for scrolling.
	 *
	 * @private
	 * @memberof ItemDrag.prototype
	 */
	ItemDrag.prototype._prepareScroll = function() {
	  var item = this._item;
  
	  // If item is not active do nothing.
	  if (!item._isActive) return;
  
	  var element = item._element;
	  var grid = this._getGrid();
	  var settings = grid._settings;
	  var axis = settings.dragAxis;
	  var gridContainer = grid._element;
	  var offsetDiff;
  
	  // Calculate element's rect and x/y diff.
	  var rect = element.getBoundingClientRect();
	  var xDiff = this._elementClientX - rect.left;
	  var yDiff = this._elementClientY - rect.top;
  
	  // Update container diff.
	  if (this._container !== gridContainer) {
		offsetDiff = getOffsetDiff(this._containingBlock, gridContainer);
		this._containerDiffX = offsetDiff.left;
		this._containerDiffY = offsetDiff.top;
	  }
  
	  // Update horizontal position data.
	  if (axis !== 'y') {
		this._left += xDiff;
		this._gridX = this._left - this._containerDiffX;
	  }
  
	  // Update vertical position data.
	  if (axis !== 'x') {
		this._top += yDiff;
		this._gridY = this._top - this._containerDiffY;
	  }
  
	  // Overlap handling.
	  if (settings.dragSort) this._checkOverlapDebounce();
	};
  
	/**
	 * Apply scroll to dragged item.
	 *
	 * @private
	 * @memberof ItemDrag.prototype
	 */
	ItemDrag.prototype._applyScroll = function() {
	  var item = this._item;
  
	  // If item is not active do nothing.
	  if (!item._isActive) return;
  
	  // Update element's translateX/Y values.
	  item._element.style[transformProp] = getTranslateString(this._left, this._top);
  
	  // Emit dragScroll event.
	  this._getGrid()._emit(eventDragScroll, item, this._lastScrollEvent);
	};
  
	/**
	 * Drag end handler.
	 *
	 * @private
	 * @memberof ItemDrag.prototype
	 * @param {Object} event
	 */
	ItemDrag.prototype._onEnd = function(event) {
	  var item = this._item;
	  var element = item._element;
	  var grid = this._getGrid();
	  var settings = grid._settings;
	  var release = item._release;
  
	  // If item is not active, reset drag.
	  if (!item._isActive) {
		this.stop();
		return;
	  }
  
	  // Cancel queued move and scroll ticks.
	  cancelMoveTick(item._id);
	  cancelScrollTick(item._id);
  
	  // Finish currently queued overlap check.
	  settings.dragSort && this._checkOverlapDebounce('finish');
  
	  // Remove scroll listeners.
	  this._unbindScrollListeners();
  
	  // Setup release data.
	  release._containerDiffX = this._containerDiffX;
	  release._containerDiffY = this._containerDiffY;
  
	  // Reset drag data.
	  this._reset();
  
	  // Remove drag class name from element.
	  removeClass(element, settings.itemDraggingClass);
  
	  // Emit dragEnd event.
	  grid._emit(eventDragEnd, item, event);
  
	  // Finish up the migration process or start the release process.
	  this._isMigrating ? this._finishMigration() : release.start();
	};
  
	/**
	 * Private helpers
	 * ***************
	 */
  
	/**
	 * Prevent default.
	 *
	 * @param {Object} e
	 */
	function preventDefault(e) {
	  if (e.preventDefault) e.preventDefault();
	}
  
	/**
	 * Calculate how many percent the intersection area of two rectangles is from
	 * the maximum potential intersection area between the rectangles.
	 *
	 * @param {Rectangle} a
	 * @param {Rectangle} b
	 * @returns {Number}
	 *   - A number between 0-100.
	 */
	function getRectOverlapScore(a, b) {
	  // Return 0 immediately if the rectangles do not overlap.
	  if (
		a.left + a.width <= b.left ||
		b.left + b.width <= a.left ||
		a.top + a.height <= b.top ||
		b.top + b.height <= a.top
	  ) {
		return 0;
	  }
  
	  // Calculate intersection area's width, height, max height and max width.
	  var width = Math.min(a.left + a.width, b.left + b.width) - Math.max(a.left, b.left);
	  var height = Math.min(a.top + a.height, b.top + b.height) - Math.max(a.top, b.top);
	  var maxWidth = Math.min(a.width, b.width);
	  var maxHeight = Math.min(a.height, b.height);
  
	  return ((width * height) / (maxWidth * maxHeight)) * 100;
	}
  
	/**
	 * Get element's scroll parents.
	 *
	 * @param {HTMLElement} element
	 * @param {Array} [data]
	 * @returns {HTMLElement[]}
	 */
	function getScrollParents(element, data) {
	  var ret = data || [];
	  var parent = element.parentNode;
  
	  //
	  // If transformed elements leak fixed elements.
	  //
  
	  if (hasTransformLeak) {
		// If the element is fixed it can not have any scroll parents.
		if (getStyle(element, 'position') === 'fixed') return ret;
  
		// Find scroll parents.
		while (parent && parent !== document && parent !== document.documentElement) {
		  if (isScrollable(parent)) ret.push(parent);
		  parent = getStyle(parent, 'position') === 'fixed' ? null : parent.parentNode;
		}
  
		// If parent is not fixed element, add window object as the last scroll
		// parent.
		parent !== null && ret.push(window);
		return ret;
	  }
  
	  //
	  // If fixed elements behave as defined in the W3C specification.
	  //
  
	  // Find scroll parents.
	  while (parent && parent !== document) {
		// If the currently looped element is fixed ignore all parents that are
		// not transformed.
		if (getStyle(element, 'position') === 'fixed' && !isTransformed(parent)) {
		  parent = parent.parentNode;
		  continue;
		}
  
		// Add the parent element to return items if it is scrollable.
		if (isScrollable(parent)) ret.push(parent);
  
		// Update element and parent references.
		element = parent;
		parent = parent.parentNode;
	  }
  
	  // If the last item is the root element, replace it with window. The root
	  // element scroll is propagated to the window.
	  if (ret[ret.length - 1] === document.documentElement) {
		ret[ret.length - 1] = window;
	  }
	  // Otherwise add window as the last scroll parent.
	  else {
		ret.push(window);
	  }
  
	  return ret;
	}
  
	/**
	 * Check if an element is scrollable.
	 *
	 * @param {HTMLElement} element
	 * @returns {Boolean}
	 */
	function isScrollable(element) {
	  var overflow = getStyle(element, 'overflow');
	  if (overflow === 'auto' || overflow === 'scroll') return true;
  
	  overflow = getStyle(element, 'overflow-x');
	  if (overflow === 'auto' || overflow === 'scroll') return true;
  
	  overflow = getStyle(element, 'overflow-y');
	  if (overflow === 'auto' || overflow === 'scroll') return true;
  
	  return false;
	}
  
	/**
	 * Check if drag gesture can be interpreted as a click, based on final drag
	 * event data.
	 *
	 * @param {Object} element
	 * @returns {Boolean}
	 */
	function isClick(event) {
	  return Math.abs(event.deltaX) < 2 && Math.abs(event.deltaY) < 2 && event.deltaTime < 200;
	}
  
	/**
	 * Check if an element is an anchor element and open the href url if possible.
	 *
	 * @param {HTMLElement} element
	 */
	function openAnchorHref(element) {
	  // Make sure the element is anchor element.
	  if (element.tagName.toLowerCase() !== 'a') return;
  
	  // Get href and make sure it exists.
	  var href = element.getAttribute('href');
	  if (!href) return;
  
	  // Finally let's navigate to the link href.
	  var target = element.getAttribute('target');
	  if (target && target !== '_self') {
		window.open(href, target);
	  } else {
		window.location.href = href;
	  }
	}
  
	/**
	 * Detects if transformed elements leak fixed elements. According W3C
	 * transform rendering spec a transformed element should contain even fixed
	 * elements. Meaning that fixed elements are positioned relative to the
	 * closest transformed ancestor element instead of window. However, not every
	 * browser follows the spec (IE and older Firefox). So we need to test it.
	 * https://www.w3.org/TR/css3-2d-transforms/#transform-rendering
	 *
	 * Borrowed from Mezr (v0.6.1):
	 * https://github.com/niklasramo/mezr/blob/0.6.1/mezr.js#L607
	 */
	function checkTransformLeak() {
	  // No transforms -> definitely leaks.
	  if (!isTransformSupported) return true;
  
	  // No body available -> can't check it.
	  if (!document.body) return null;
  
	  // Do the test.
	  var elems = [0, 1].map(function(elem, isInner) {
		elem = document.createElement('div');
		elem.style.position = isInner ? 'fixed' : 'absolute';
		elem.style.display = 'block';
		elem.style.visibility = 'hidden';
		elem.style.left = isInner ? '0px' : '1px';
		elem.style[transformProp] = 'none';
		return elem;
	  });
	  var outer = document.body.appendChild(elems[0]);
	  var inner = outer.appendChild(elems[1]);
	  var left = inner.getBoundingClientRect().left;
	  outer.style[transformProp] = 'scale(1)';
	  var ret = left === inner.getBoundingClientRect().left;
	  document.body.removeChild(outer);
	  return ret;
	}
  
	/**
	 * Queue constructor.
	 *
	 * @class
	 */
	function Queue() {
	  this._queue = [];
	  this._isDestroyed = false;
	}
  
	/**
	 * Public prototype methods
	 * ************************
	 */
  
	/**
	 * Add callback to the queue.
	 *
	 * @public
	 * @memberof Queue.prototype
	 * @param {Function} callback
	 * @returns {Queue}
	 */
	Queue.prototype.add = function(callback) {
	  if (this._isDestroyed) return this;
	  this._queue.push(callback);
	  return this;
	};
  
	/**
	 * Process queue callbacks and reset the queue.
	 *
	 * @public
	 * @memberof Queue.prototype
	 * @param {*} arg1
	 * @param {*} arg2
	 * @returns {Queue}
	 */
	Queue.prototype.flush = function(arg1, arg2) {
	  if (this._isDestroyed) return this;
  
	  var queue = this._queue;
	  var length = queue.length;
	  var i;
  
	  // Quit early if the queue is empty.
	  if (!length) return this;
  
	  var singleCallback = length === 1;
	  var snapshot = singleCallback ? queue[0] : queue.slice(0);
  
	  // Reset queue.
	  queue.length = 0;
  
	  // If we only have a single callback let's just call it.
	  if (singleCallback) {
		snapshot(arg1, arg2);
		return this;
	  }
  
	  // If we have multiple callbacks, let's process them.
	  for (i = 0; i < length; i++) {
		snapshot[i](arg1, arg2);
		if (this._isDestroyed) break;
	  }
  
	  return this;
	};
  
	/**
	 * Destroy Queue instance.
	 *
	 * @public
	 * @memberof Queue.prototype
	 * @returns {Queue}
	 */
	Queue.prototype.destroy = function() {
	  if (this._isDestroyed) return this;
  
	  this._isDestroyed = true;
	  this._queue.length = 0;
  
	  return this;
	};
  
	/**
	 * Layout manager for Item instance.
	 *
	 * @class
	 * @param {Item} item
	 */
	function ItemLayout(item) {
	  this._item = item;
	  this._isActive = false;
	  this._isDestroyed = false;
	  this._isInterrupted = false;
	  this._currentStyles = {};
	  this._targetStyles = {};
	  this._currentLeft = 0;
	  this._currentTop = 0;
	  this._offsetLeft = 0;
	  this._offsetTop = 0;
	  this._skipNextAnimation = false;
	  this._animateOptions = {
		onFinish: this._finish.bind(this)
	  };
	  this._queue = new Queue();
  
	  // Bind animation handlers and finish method.
	  this._setupAnimation = this._setupAnimation.bind(this);
	  this._startAnimation = this._startAnimation.bind(this);
	}
  
	/**
	 * Public prototype methods
	 * ************************
	 */
  
	/**
	 * Start item layout based on it's current data.
	 *
	 * @public
	 * @memberof ItemLayout.prototype
	 * @param {Boolean} [instant=false]
	 * @param {Function} [onFinish]
	 * @returns {ItemLayout}
	 */
	ItemLayout.prototype.start = function(instant, onFinish) {
	  if (this._isDestroyed) return;
  
	  var item = this._item;
	  var element = item._element;
	  var release = item._release;
	  var gridSettings = item.getGrid()._settings;
	  var isPositioning = this._isActive;
	  var isJustReleased = release._isActive && release._isPositioningStarted === false;
	  var animDuration = isJustReleased
		? gridSettings.dragReleaseDuration
		: gridSettings.layoutDuration;
	  var animEasing = isJustReleased ? gridSettings.dragReleaseEasing : gridSettings.layoutEasing;
	  var animEnabled = !instant && !this._skipNextAnimation && animDuration > 0;
	  var isAnimating;
  
	  // If the item is currently positioning process current layout callback
	  // queue with interrupted flag on.
	  if (isPositioning) this._queue.flush(true, item);
  
	  // Mark release positioning as started.
	  if (isJustReleased) release._isPositioningStarted = true;
  
	  // Push the callback to the callback queue.
	  if (typeof onFinish === 'function') this._queue.add(onFinish);
  
	  // If no animations are needed, easy peasy!
	  if (!animEnabled) {
		this._updateOffsets();
		this._updateTargetStyles();
		isPositioning && cancelLayoutTick(item._id);
		isAnimating = item._animate.isAnimating();
		this.stop(false, this._targetStyles);
		!isAnimating && setStyles(element, this._targetStyles);
		this._skipNextAnimation = false;
		return this._finish();
	  }
  
	  // Set item active and store some data for the animation that is about to be
	  // triggered.
	  this._isActive = true;
	  this._animateOptions.easing = animEasing;
	  this._animateOptions.duration = animDuration;
	  this._isInterrupted = isPositioning;
  
	  // Start the item's layout animation in the next tick.
	  addLayoutTick(item._id, this._setupAnimation, this._startAnimation);
  
	  return this;
	};
  
	/**
	 * Stop item's position animation if it is currently animating.
	 *
	 * @public
	 * @memberof ItemLayout.prototype
	 * @param {Boolean} [processCallbackQueue=false]
	 * @param {Object} [targetStyles]
	 * @returns {ItemLayout}
	 */
	ItemLayout.prototype.stop = function(processCallbackQueue, targetStyles) {
	  if (this._isDestroyed || !this._isActive) return this;
  
	  var item = this._item;
  
	  // Cancel animation init.
	  cancelLayoutTick(item._id);
  
	  // Stop animation.
	  item._animate.stop(targetStyles);
  
	  // Remove positioning class.
	  removeClass(item._element, item.getGrid()._settings.itemPositioningClass);
  
	  // Reset active state.
	  this._isActive = false;
  
	  // Process callback queue if needed.
	  if (processCallbackQueue) this._queue.flush(true, item);
  
	  return this;
	};
  
	/**
	 * Destroy the instance and stop current animation if it is running.
	 *
	 * @public
	 * @memberof ItemLayout.prototype
	 * @returns {ItemLayout}
	 */
	ItemLayout.prototype.destroy = function() {
	  if (this._isDestroyed) return this;
	  this.stop(true, {});
	  this._queue.destroy();
	  this._item = this._currentStyles = this._targetStyles = this._animateOptions = null;
	  this._isDestroyed = true;
	  return this;
	};
  
	/**
	 * Private prototype methods
	 * *************************
	 */
  
	/**
	 * Calculate and update item's current layout offset data.
	 *
	 * @private
	 * @memberof ItemLayout.prototype
	 */
	ItemLayout.prototype._updateOffsets = function() {
	  if (this._isDestroyed) return;
  
	  var item = this._item;
	  var migrate = item._migrate;
	  var release = item._release;
  
	  this._offsetLeft = release._isActive
		? release._containerDiffX
		: migrate._isActive
		  ? migrate._containerDiffX
		  : 0;
  
	  this._offsetTop = release._isActive
		? release._containerDiffY
		: migrate._isActive
		  ? migrate._containerDiffY
		  : 0;
	};
  
	/**
	 * Calculate and update item's layout target styles.
	 *
	 * @private
	 * @memberof ItemLayout.prototype
	 */
	ItemLayout.prototype._updateTargetStyles = function() {
	  if (this._isDestroyed) return;
  
	  var item = this._item;
  
	  this._targetStyles.transform = getTranslateString(
		item._left + this._offsetLeft,
		item._top + this._offsetTop
	  );
	};
  
	/**
	 * Finish item layout procedure.
	 *
	 * @private
	 * @memberof ItemLayout.prototype
	 */
	ItemLayout.prototype._finish = function() {
	  if (this._isDestroyed) return;
  
	  var item = this._item;
	  var migrate = item._migrate;
	  var release = item._release;
  
	  // Mark the item as inactive and remove positioning classes.
	  if (this._isActive) {
		this._isActive = false;
		removeClass(item._element, item.getGrid()._settings.itemPositioningClass);
	  }
  
	  // Finish up release and migration.
	  if (release._isActive) release.stop();
	  if (migrate._isActive) migrate.stop();
  
	  // Process the callback queue.
	  this._queue.flush(false, item);
	};
  
	/**
	 * Prepare item for layout animation.
	 *
	 * @private
	 * @memberof ItemLayout.prototype
	 */
	ItemLayout.prototype._setupAnimation = function() {
	  var element = this._item._element;
	  var translate = getTranslate(element);
	  this._currentLeft = translate.x;
	  this._currentTop = translate.y;
	};
  
	/**
	 * Start layout animation.
	 *
	 * @private
	 * @memberof ItemLayout.prototype
	 */
	ItemLayout.prototype._startAnimation = function() {
	  var item = this._item;
	  var element = item._element;
	  var grid = item.getGrid();
	  var settings = grid._settings;
  
	  // Let's update the offset data and target styles.
	  this._updateOffsets();
	  this._updateTargetStyles();
  
	  // If the item is already in correct position let's quit early.
	  if (
		item._left === this._currentLeft - this._offsetLeft &&
		item._top === this._currentTop - this._offsetTop
	  ) {
		if (this._isInterrupted) this.stop(false, this._targetStyles);
		this._isActive = false;
		this._finish();
		return;
	  }
  
	  // Set item's positioning class if needed.
	  !this._isInterrupted && addClass(element, settings.itemPositioningClass);
  
	  // Get current styles for animation.
	  this._currentStyles.transform = getTranslateString(this._currentLeft, this._currentTop);
  
	  // Animate.
	  item._animate.start(this._currentStyles, this._targetStyles, this._animateOptions);
	};
  
	var tempStyles = {};
  
	/**
	 * The migrate process handler constructor.
	 *
	 * @class
	 * @param {Item} item
	 */
	function ItemMigrate(item) {
	  // Private props.
	  this._item = item;
	  this._isActive = false;
	  this._isDestroyed = false;
	  this._container = false;
	  this._containerDiffX = 0;
	  this._containerDiffY = 0;
	}
  
	/**
	 * Public prototype methods
	 * ************************
	 */
  
	/**
	 * Start the migrate process of an item.
	 *
	 * @public
	 * @memberof ItemMigrate.prototype
	 * @param {Grid} targetGrid
	 * @param {GridSingleItemQuery} position
	 * @param {HTMLElement} [container]
	 * @returns {ItemMigrate}
	 */
	ItemMigrate.prototype.start = function(targetGrid, position, container) {
	  if (this._isDestroyed) return this;
  
	  var item = this._item;
	  var element = item._element;
	  var isVisible = item.isVisible();
	  var grid = item.getGrid();
	  var settings = grid._settings;
	  var targetSettings = targetGrid._settings;
	  var targetElement = targetGrid._element;
	  var targetItems = targetGrid._items;
	  var currentIndex = grid._items.indexOf(item);
	  var targetContainer = container || document.body;
	  var targetIndex;
	  var targetItem;
	  var currentContainer;
	  var offsetDiff;
	  var containerDiff;
	  var translate;
	  var translateX;
	  var translateY;
  
	  // Get target index.
	  if (typeof position === 'number') {
		targetIndex = normalizeArrayIndex(targetItems, position, true);
	  } else {
		targetItem = targetGrid._getItem(position);
		/** @todo Consider throwing an error here instead of silently failing. */
		if (!targetItem) return this;
		targetIndex = targetItems.indexOf(targetItem);
	  }
  
	  // Get current translateX and translateY values if needed.
	  if (item.isPositioning() || this._isActive || item.isReleasing()) {
		translate = getTranslate(element);
		translateX = translate.x;
		translateY = translate.y;
	  }
  
	  // Abort current positioning.
	  if (item.isPositioning()) {
		item._layout.stop(true, { transform: getTranslateString(translateX, translateY) });
	  }
  
	  // Abort current migration.
	  if (this._isActive) {
		translateX -= this._containerDiffX;
		translateY -= this._containerDiffY;
		this.stop(true, { transform: getTranslateString(translateX, translateY) });
	  }
  
	  // Abort current release.
	  if (item.isReleasing()) {
		translateX -= item._release._containerDiffX;
		translateY -= item._release._containerDiffY;
		item._release.stop(true, { transform: getTranslateString(translateX, translateY) });
	  }
  
	  // Stop current visibility animations.
	  item._visibility._stopAnimation();
  
	  // Destroy current drag.
	  if (item._drag) item._drag.destroy();
  
	  // Process current visibility animation queue.
	  item._visibility._queue.flush(true, item);
  
	  // Emit beforeSend event.
	  if (grid._hasListeners(eventBeforeSend)) {
		grid._emit(eventBeforeSend, {
		  item: item,
		  fromGrid: grid,
		  fromIndex: currentIndex,
		  toGrid: targetGrid,
		  toIndex: targetIndex
		});
	  }
  
	  // Emit beforeReceive event.
	  if (targetGrid._hasListeners(eventBeforeReceive)) {
		targetGrid._emit(eventBeforeReceive, {
		  item: item,
		  fromGrid: grid,
		  fromIndex: currentIndex,
		  toGrid: targetGrid,
		  toIndex: targetIndex
		});
	  }
  
	  // Remove current classnames.
	  removeClass(element, settings.itemClass);
	  removeClass(element, settings.itemVisibleClass);
	  removeClass(element, settings.itemHiddenClass);
  
	  // Add new classnames.
	  addClass(element, targetSettings.itemClass);
	  addClass(element, isVisible ? targetSettings.itemVisibleClass : targetSettings.itemHiddenClass);
  
	  // Move item instance from current grid to target grid.
	  grid._items.splice(currentIndex, 1);
	  arrayInsert(targetItems, item, targetIndex);
  
	  // Update item's grid id reference.
	  item._gridId = targetGrid._id;
  
	  // Get current container.
	  currentContainer = element.parentNode;
  
	  // Move the item inside the target container if it's different than the
	  // current container.
	  if (targetContainer !== currentContainer) {
		targetContainer.appendChild(element);
		offsetDiff = getOffsetDiff(targetContainer, currentContainer, true);
		if (!translate) {
		  translate = getTranslate(element);
		  translateX = translate.x;
		  translateY = translate.y;
		}
		element.style[transformProp] = getTranslateString(
		  translateX + offsetDiff.left,
		  translateY + offsetDiff.top
		);
	  }
  
	  // Update child element's styles to reflect the current visibility state.
	  item._child.removeAttribute('style');
	  setStyles(item._child, isVisible ? targetSettings.visibleStyles : targetSettings.hiddenStyles);
  
	  // Update display style.
	  element.style.display = isVisible ? 'block' : 'hidden';
  
	  // Get offset diff for the migration data.
	  containerDiff = getOffsetDiff(targetContainer, targetElement, true);
  
	  // Update item's cached dimensions and sort data.
	  item._refreshDimensions();
	  item._refreshSortData();
  
	  // Create new drag handler.
	  item._drag = targetSettings.dragEnabled ? new ItemDrag(item) : null;
  
	  // Setup migration data.
	  this._isActive = true;
	  this._container = targetContainer;
	  this._containerDiffX = containerDiff.left;
	  this._containerDiffY = containerDiff.top;
  
	  // Emit send event.
	  if (grid._hasListeners(eventSend)) {
		grid._emit(eventSend, {
		  item: item,
		  fromGrid: grid,
		  fromIndex: currentIndex,
		  toGrid: targetGrid,
		  toIndex: targetIndex
		});
	  }
  
	  // Emit receive event.
	  if (targetGrid._hasListeners(eventReceive)) {
		targetGrid._emit(eventReceive, {
		  item: item,
		  fromGrid: grid,
		  fromIndex: currentIndex,
		  toGrid: targetGrid,
		  toIndex: targetIndex
		});
	  }
  
	  return this;
	};
  
	/**
	 * End the migrate process of an item. This method can be used to abort an
	 * ongoing migrate process (animation) or finish the migrate process.
	 *
	 * @public
	 * @memberof ItemMigrate.prototype
	 * @param {Boolean} [abort=false]
	 *  - Should the migration be aborted?
	 * @param {Object} [currentStyles]
	 *  - Optional current translateX and translateY styles.
	 * @returns {ItemMigrate}
	 */
	ItemMigrate.prototype.stop = function(abort, currentStyles) {
	  if (this._isDestroyed || !this._isActive) return this;
  
	  var item = this._item;
	  var element = item._element;
	  var grid = item.getGrid();
	  var gridElement = grid._element;
	  var translate;
  
	  if (this._container !== gridElement) {
		if (!currentStyles) {
		  if (abort) {
			translate = getTranslate(element);
			tempStyles.transform = getTranslateString(
			  translate.x - this._containerDiffX,
			  translate.y - this._containerDiffY
			);
		  } else {
			tempStyles.transform = getTranslateString(item._left, item._top);
		  }
		  currentStyles = tempStyles;
		}
		gridElement.appendChild(element);
		setStyles(element, currentStyles);
	  }
  
	  this._isActive = false;
	  this._container = null;
	  this._containerDiffX = 0;
	  this._containerDiffY = 0;
  
	  return this;
	};
  
	/**
	 * Destroy instance.
	 *
	 * @public
	 * @memberof ItemMigrate.prototype
	 * @returns {ItemMigrate}
	 */
	ItemMigrate.prototype.destroy = function() {
	  if (this._isDestroyed) return this;
	  this.stop(true);
	  this._item = null;
	  this._isDestroyed = true;
	  return this;
	};
  
	var tempStyles$1 = {};
  
	/**
	 * The release process handler constructor. Although this might seem as proper
	 * fit for the drag process this needs to be separated into it's own logic
	 * because there might be a scenario where drag is disabled, but the release
	 * process still needs to be implemented (dragging from a grid to another).
	 *
	 * @class
	 * @param {Item} item
	 */
	function ItemRelease(item) {
	  this._item = item;
	  this._isActive = false;
	  this._isDestroyed = false;
	  this._isPositioningStarted = false;
	  this._containerDiffX = 0;
	  this._containerDiffY = 0;
	}
  
	/**
	 * Public prototype methods
	 * ************************
	 */
  
	/**
	 * Start the release process of an item.
	 *
	 * @public
	 * @memberof ItemRelease.prototype
	 * @returns {ItemRelease}
	 */
	ItemRelease.prototype.start = function() {
	  if (this._isDestroyed || this._isActive) return this;
  
	  var item = this._item;
	  var grid = item.getGrid();
  
	  // Flag release as active.
	  this._isActive = true;
  
	  // Add release class name to the released element.
	  addClass(item._element, grid._settings.itemReleasingClass);
  
	  // Emit dragReleaseStart event.
	  grid._emit(eventDragReleaseStart, item);
  
	  // Position the released item.
	  item._layout.start(false);
  
	  return this;
	};
  
	/**
	 * End the release process of an item. This method can be used to abort an
	 * ongoing release process (animation) or finish the release process.
	 *
	 * @public
	 * @memberof ItemRelease.prototype
	 * @param {Boolean} [abort=false]
	 *  - Should the release be aborted? When true, the release end event won't be
	 *    emitted. Set to true only when you need to abort the release process
	 *    while the item is animating to it's position.
	 * @param {Object} [currentStyles]
	 *  - Optional current translateX and translateY styles.
	 * @returns {ItemRelease}
	 */
	ItemRelease.prototype.stop = function(abort, currentStyles) {
	  if (this._isDestroyed || !this._isActive) return this;
  
	  var item = this._item;
	  var element = item._element;
	  var grid = item.getGrid();
	  var container = grid._element;
	  var translate;
  
	  // Reset data and remove releasing class name from the element.
	  this._reset();
  
	  // If the released element is outside the grid's container element put it
	  // back there and adjust position accordingly.
	  if (element.parentNode !== container) {
		if (!currentStyles) {
		  if (abort) {
			translate = getTranslate(element);
			tempStyles$1.transform = getTranslateString(
			  translate.x - this._containerDiffX,
			  translate.y - this._containerDiffY
			);
		  } else {
			tempStyles$1.transform = getTranslateString(item._left, item._top);
		  }
		  currentStyles = tempStyles$1;
		}
		container.appendChild(element);
		setStyles(element, currentStyles);
	  }
  
	  // Emit dragReleaseEnd event.
	  if (!abort) grid._emit(eventDragReleaseEnd, item);
  
	  return this;
	};
  
	/**
	 * Destroy instance.
	 *
	 * @public
	 * @memberof ItemRelease.prototype
	 * @returns {ItemRelease}
	 */
	ItemRelease.prototype.destroy = function() {
	  if (this._isDestroyed) return this;
	  this.stop(true);
	  this._item = null;
	  this._isDestroyed = true;
	  return this;
	};
  
	/**
	 * Private prototype methods
	 * *************************
	 */
  
	/**
	 * Reset public data and remove releasing class.
	 *
	 * @private
	 * @memberof ItemRelease.prototype
	 */
	ItemRelease.prototype._reset = function() {
	  if (this._isDestroyed) return;
	  var item = this._item;
	  this._isActive = false;
	  this._isPositioningStarted = false;
	  this._containerDiffX = 0;
	  this._containerDiffY = 0;
	  removeClass(item._element, item.getGrid()._settings.itemReleasingClass);
	};
  
	/**
	 * Get current values of the provided styles definition object.
	 *
	 * @param {HTMLElement} element
	 * @param {Object} styles
	 * @return {Object}
	 */
	function getCurrentStyles(element, styles) {
	  var current = {};
	  for (var prop in styles) {
		current[prop] = getStyle(element, getStyleName(prop));
	  }
	  return current;
	}
  
	/**
	 * Visibility manager for Item instance.
	 *
	 * @class
	 * @param {Item} item
	 */
	function ItemVisibility(item) {
	  var isActive = item._isActive;
	  var element = item._element;
	  var settings = item.getGrid()._settings;
  
	  this._item = item;
	  this._isDestroyed = false;
  
	  // Set up visibility states.
	  this._isHidden = !isActive;
	  this._isHiding = false;
	  this._isShowing = false;
  
	  // Callback queue.
	  this._queue = new Queue();
  
	  // Bind show/hide finishers.
	  this._finishShow = this._finishShow.bind(this);
	  this._finishHide = this._finishHide.bind(this);
  
	  // Force item to be either visible or hidden on init.
	  element.style.display = isActive ? 'block' : 'none';
  
	  // Set visible/hidden class.
	  addClass(element, isActive ? settings.itemVisibleClass : settings.itemHiddenClass);
  
	  // Set initial styles for the child element.
	  setStyles(item._child, isActive ? settings.visibleStyles : settings.hiddenStyles);
	}
  
	/**
	 * Public prototype methods
	 * ************************
	 */
  
	/**
	 * Show item.
	 *
	 * @public
	 * @memberof ItemVisibility.prototype
	 * @param {Boolean} instant
	 * @param {Function} [onFinish]
	 * @returns {ItemVisibility}
	 */
	ItemVisibility.prototype.show = function(instant, onFinish) {
	  if (this._isDestroyed) return this;
  
	  var item = this._item;
	  var element = item._element;
	  var queue = this._queue;
	  var callback = typeof onFinish === 'function' ? onFinish : null;
	  var grid = item.getGrid();
	  var settings = grid._settings;
  
	  // If item is visible call the callback and be done with it.
	  if (!this._isShowing && !this._isHidden) {
		callback && callback(false, item);
		return this;
	  }
  
	  // If item is showing and does not need to be shown instantly, let's just
	  // push callback to the callback queue and be done with it.
	  if (this._isShowing && !instant) {
		callback && queue.add(callback);
		return this;
	  }
  
	  // If the item is hiding or hidden process the current visibility callback
	  // queue with the interrupted flag active, update classes and set display
	  // to block if necessary.
	  if (!this._isShowing) {
		queue.flush(true, item);
		removeClass(element, settings.itemHiddenClass);
		addClass(element, settings.itemVisibleClass);
		if (!this._isHiding) element.style.display = 'block';
	  }
  
	  // Push callback to the callback queue.
	  callback && queue.add(callback);
  
	  // Update visibility states.
	  item._isActive = this._isShowing = true;
	  this._isHiding = this._isHidden = false;
  
	  // Finally let's start show animation.
	  this._startAnimation(true, instant, this._finishShow);
  
	  return this;
	};
  
	/**
	 * Hide item.
	 *
	 * @public
	 * @memberof ItemVisibility.prototype
	 * @param {Boolean} instant
	 * @param {Function} [onFinish]
	 * @returns {ItemVisibility}
	 */
	ItemVisibility.prototype.hide = function(instant, onFinish) {
	  if (this._isDestroyed) return this;
  
	  var item = this._item;
	  var element = item._element;
	  var queue = this._queue;
	  var callback = typeof onFinish === 'function' ? onFinish : null;
	  var grid = item.getGrid();
	  var settings = grid._settings;
  
	  // If item is already hidden call the callback and be done with it.
	  if (!this._isHiding && this._isHidden) {
		callback && callback(false, item);
		return this;
	  }
  
	  // If item is hiding and does not need to be hidden instantly, let's just
	  // push callback to the callback queue and be done with it.
	  if (this._isHiding && !instant) {
		callback && queue.add(callback);
		return this;
	  }
  
	  // If the item is showing or visible process the current visibility callback
	  // queue with the interrupted flag active, update classes and set display
	  // to block if necessary.
	  if (!this._isHiding) {
		queue.flush(true, item);
		addClass(element, settings.itemHiddenClass);
		removeClass(element, settings.itemVisibleClass);
	  }
  
	  // Push callback to the callback queue.
	  callback && queue.add(callback);
  
	  // Update visibility states.
	  this._isHidden = this._isHiding = true;
	  item._isActive = this._isShowing = false;
  
	  // Finally let's start hide animation.
	  this._startAnimation(false, instant, this._finishHide);
  
	  return this;
	};
  
	/**
	 * Destroy the instance and stop current animation if it is running.
	 *
	 * @public
	 * @memberof ItemVisibility.prototype
	 * @returns {ItemVisibility}
	 */
	ItemVisibility.prototype.destroy = function() {
	  if (this._isDestroyed) return this;
  
	  var item = this._item;
	  var element = item._element;
	  var grid = item.getGrid();
	  var queue = this._queue;
	  var settings = grid._settings;
  
	  // Stop visibility animation.
	  this._stopAnimation({});
  
	  // Fire all uncompleted callbacks with interrupted flag and destroy the queue.
	  queue.flush(true, item).destroy();
  
	  // Remove visible/hidden classes.
	  removeClass(element, settings.itemVisibleClass);
	  removeClass(element, settings.itemHiddenClass);
  
	  // Reset state.
	  this._item = null;
	  this._isHiding = this._isShowing = false;
	  this._isDestroyed = this._isHidden = true;
  
	  return this;
	};
  
	/**
	 * Private prototype methods
	 * *************************
	 */
  
	/**
	 * Start visibility animation.
	 *
	 * @private
	 * @memberof ItemVisibility.prototype
	 * @param {Boolean} toVisible
	 * @param {Boolean} [instant]
	 * @param {Function} [onFinish]
	 */
	ItemVisibility.prototype._startAnimation = function(toVisible, instant, onFinish) {
	  if (this._isDestroyed) return;
  
	  var item = this._item;
	  var settings = item.getGrid()._settings;
	  var targetStyles = toVisible ? settings.visibleStyles : settings.hiddenStyles;
	  var duration = parseInt(toVisible ? settings.showDuration : settings.hideDuration) || 0;
	  var easing = (toVisible ? settings.showEasing : settings.hideEasing) || 'ease';
	  var isInstant = instant || duration <= 0;
	  var currentStyles;
  
	  // No target styles? Let's quit early.
	  if (!targetStyles) {
		onFinish && onFinish();
		return;
	  }
  
	  // Cancel queued visibility tick.
	  cancelVisibilityTick(item._id);
  
	  // If we need to apply the styles instantly without animation.
	  if (isInstant) {
		if (item._animateChild.isAnimating()) {
		  item._animateChild.stop(targetStyles);
		} else {
		  setStyles(item._child, targetStyles);
		}
		onFinish && onFinish();
		return;
	  }
  
	  // Start the animation in the next tick (to avoid layout thrashing).
	  addVisibilityTick(
		item._id,
		function() {
		  currentStyles = getCurrentStyles(item._child, targetStyles);
		},
		function() {
		  item._animateChild.start(currentStyles, targetStyles, {
			duration: duration,
			easing: easing,
			onFinish: onFinish
		  });
		}
	  );
	};
  
	/**
	 * Stop visibility animation.
	 *
	 * @private
	 * @memberof ItemVisibility.prototype
	 * @param {Object} [targetStyles]
	 */
	ItemVisibility.prototype._stopAnimation = function(targetStyles) {
	  if (this._isDestroyed) return;
	  var item = this._item;
	  cancelVisibilityTick(item._id);
	  item._animateChild.stop(targetStyles);
	};
  
	/**
	 * Finish show procedure.
	 *
	 * @private
	 * @memberof ItemVisibility.prototype
	 */
	ItemVisibility.prototype._finishShow = function() {
	  if (this._isHidden) return;
	  this._isShowing = false;
	  this._queue.flush(false, this._item);
	};
  
	/**
	 * Finish hide procedure.
	 *
	 * @private
	 * @memberof ItemVisibility.prototype
	 */
	var finishStyles = {};
	ItemVisibility.prototype._finishHide = function() {
	  if (!this._isHidden) return;
	  var item = this._item;
	  this._isHiding = false;
	  finishStyles.transform = getTranslateString(0, 0);
	  item._layout.stop(true, finishStyles);
	  item._element.style.display = 'none';
	  this._queue.flush(false, item);
	};
  
	var id = 0;
  
	/**
	 * Returns a unique numeric id (increments a base value on every call).
	 * @returns {Number}
	 */
	function createUid() {
	  return ++id;
	}
  
	/**
	 * Creates a new Item instance for a Grid instance.
	 *
	 * @class
	 * @param {Grid} grid
	 * @param {HTMLElement} element
	 * @param {Boolean} [isActive]
	 */
	function Item(grid, element, isActive) {
	  var settings = grid._settings;
  
	  // Create instance id.
	  this._id = createUid();
  
	  // Reference to connected Grid instance's id.
	  this._gridId = grid._id;
  
	  // Destroyed flag.
	  this._isDestroyed = false;
  
	  // Set up initial positions.
	  this._left = 0;
	  this._top = 0;
  
	  // The elements.
	  this._element = element;
	  this._child = element.children[0];
  
	  // If the provided item element is not a direct child of the grid container
	  // element, append it to the grid container.
	  if (element.parentNode !== grid._element) {
		grid._element.appendChild(element);
	  }
  
	  // Set item class.
	  addClass(element, settings.itemClass);
  
	  // If isActive is not defined, let's try to auto-detect it.
	  if (typeof isActive !== 'boolean') {
		isActive = getStyle(element, 'display') !== 'none';
	  }
  
	  // Set up active state (defines if the item is considered part of the layout
	  // or not).
	  this._isActive = isActive;
  
	  // Set element's initial position styles.
	  element.style.left = '0';
	  element.style.top = '0';
	  element.style[transformProp] = getTranslateString(0, 0);
  
	  // Initiate item's animation controllers.
	  this._animate = new ItemAnimate(element);
	  this._animateChild = new ItemAnimate(this._child);
  
	  // Setup visibility handler.
	  this._visibility = new ItemVisibility(this);
  
	  // Set up layout handler.
	  this._layout = new ItemLayout(this);
  
	  // Set up migration handler data.
	  this._migrate = new ItemMigrate(this);
  
	  // Set up release handler
	  this._release = new ItemRelease(this);
  
	  // Set up drag handler.
	  this._drag = settings.dragEnabled ? new ItemDrag(this) : null;
  
	  // Set up the initial dimensions and sort data.
	  this._refreshDimensions();
	  this._refreshSortData();
	}
  
	/**
	 * Public prototype methods
	 * ************************
	 */
  
	/**
	 * Get the instance grid reference.
	 *
	 * @public
	 * @memberof Item.prototype
	 * @returns {Grid}
	 */
	Item.prototype.getGrid = function() {
	  return gridInstances[this._gridId];
	};
  
	/**
	 * Get the instance element.
	 *
	 * @public
	 * @memberof Item.prototype
	 * @returns {HTMLElement}
	 */
	Item.prototype.getElement = function() {
	  return this._element;
	};
  
	/**
	 * Get instance element's cached width.
	 *
	 * @public
	 * @memberof Item.prototype
	 * @returns {Number}
	 */
	Item.prototype.getWidth = function() {
	  return this._width;
	};
  
	/**
	 * Get instance element's cached height.
	 *
	 * @public
	 * @memberof Item.prototype
	 * @returns {Number}
	 */
	Item.prototype.getHeight = function() {
	  return this._height;
	};
  
	/**
	 * Get instance element's cached margins.
	 *
	 * @public
	 * @memberof Item.prototype
	 * @returns {Object}
	 *   - The returned object contains left, right, top and bottom properties
	 *     which indicate the item element's cached margins.
	 */
	Item.prototype.getMargin = function() {
	  return {
		left: this._marginLeft,
		right: this._marginRight,
		top: this._marginTop,
		bottom: this._marginBottom
	  };
	};
  
	/**
	 * Get instance element's cached position.
	 *
	 * @public
	 * @memberof Item.prototype
	 * @returns {Object}
	 *   - The returned object contains left and top properties which indicate the
	 *     item element's cached position in the grid.
	 */
	Item.prototype.getPosition = function() {
	  return {
		left: this._left,
		top: this._top
	  };
	};
  
	/**
	 * Is the item active?
	 *
	 * @public
	 * @memberof Item.prototype
	 * @returns {Boolean}
	 */
	Item.prototype.isActive = function() {
	  return this._isActive;
	};
  
	/**
	 * Is the item visible?
	 *
	 * @public
	 * @memberof Item.prototype
	 * @returns {Boolean}
	 */
	Item.prototype.isVisible = function() {
	  return !!this._visibility && !this._visibility._isHidden;
	};
  
	/**
	 * Is the item being animated to visible?
	 *
	 * @public
	 * @memberof Item.prototype
	 * @returns {Boolean}
	 */
	Item.prototype.isShowing = function() {
	  return !!(this._visibility && this._visibility._isShowing);
	};
  
	/**
	 * Is the item being animated to hidden?
	 *
	 * @public
	 * @memberof Item.prototype
	 * @returns {Boolean}
	 */
	Item.prototype.isHiding = function() {
	  return !!(this._visibility && this._visibility._isHiding);
	};
  
	/**
	 * Is the item positioning?
	 *
	 * @public
	 * @memberof Item.prototype
	 * @returns {Boolean}
	 */
	Item.prototype.isPositioning = function() {
	  return !!(this._layout && this._layout._isActive);
	};
  
	/**
	 * Is the item being dragged?
	 *
	 * @public
	 * @memberof Item.prototype
	 * @returns {Boolean}
	 */
	Item.prototype.isDragging = function() {
	  return !!(this._drag && this._drag._isActive);
	};
  
	/**
	 * Is the item being released?
	 *
	 * @public
	 * @memberof Item.prototype
	 * @returns {Boolean}
	 */
	Item.prototype.isReleasing = function() {
	  return !!(this._release && this._release._isActive);
	};
  
	/**
	 * Is the item destroyed?
	 *
	 * @public
	 * @memberof Item.prototype
	 * @returns {Boolean}
	 */
	Item.prototype.isDestroyed = function() {
	  return this._isDestroyed;
	};
  
	/**
	 * Private prototype methods
	 * *************************
	 */
  
	/**
	 * Recalculate item's dimensions.
	 *
	 * @private
	 * @memberof Item.prototype
	 */
	Item.prototype._refreshDimensions = function() {
	  if (this._isDestroyed || this._visibility._isHidden) return;
  
	  var element = this._element;
	  var rect = element.getBoundingClientRect();
  
	  // Calculate width and height.
	  this._width = rect.width;
	  this._height = rect.height;
  
	  // Calculate margins (ignore negative margins).
	  this._marginLeft = Math.max(0, getStyleAsFloat(element, 'margin-left'));
	  this._marginRight = Math.max(0, getStyleAsFloat(element, 'margin-right'));
	  this._marginTop = Math.max(0, getStyleAsFloat(element, 'margin-top'));
	  this._marginBottom = Math.max(0, getStyleAsFloat(element, 'margin-bottom'));
	};
  
	/**
	 * Fetch and store item's sort data.
	 *
	 * @private
	 * @memberof Item.prototype
	 */
	Item.prototype._refreshSortData = function() {
	  if (this._isDestroyed) return;
  
	  var data = (this._sortData = {});
	  var getters = this.getGrid()._settings.sortData;
	  var prop;
  
	  for (prop in getters) {
		data[prop] = getters[prop](this, this._element);
	  }
	};
  
	/**
	 * Destroy item instance.
	 *
	 * @private
	 * @memberof Item.prototype
	 * @param {Boolean} [removeElement=false]
	 */
	Item.prototype._destroy = function(removeElement) {
	  if (this._isDestroyed) return;
  
	  var element = this._element;
	  var grid = this.getGrid();
	  var settings = grid._settings;
	  var index = grid._items.indexOf(this);
  
	  // Destroy handlers.
	  this._release.destroy();
	  this._migrate.destroy();
	  this._layout.destroy();
	  this._visibility.destroy();
	  this._animate.destroy();
	  this._animateChild.destroy();
	  this._drag && this._drag.destroy();
  
	  // Remove all inline styles.
	  element.removeAttribute('style');
	  this._child.removeAttribute('style');
  
	  // Remove item class.
	  removeClass(element, settings.itemClass);
  
	  // Remove item from Grid instance if it still exists there.
	  index > -1 && grid._items.splice(index, 1);
  
	  // Remove element from DOM.
	  removeElement && element.parentNode.removeChild(element);
  
	  // Reset state.
	  this._isActive = false;
	  this._isDestroyed = true;
	};
  
	/**
	 * This is the default layout algorithm for Muuri. Based on MAXRECTS approach
	 * as described by Jukka Jylänki in his survey: "A Thousand Ways to Pack the
	 * Bin - A Practical Approach to Two-Dimensional Rectangle Bin Packing.".
	 *
	 * @class
	 */
	function Packer() {
	  this._slots = [];
	  this._slotSizes = [];
	  this._freeSlots = [];
	  this._newSlots = [];
	  this._rectItem = {};
	  this._rectStore = [];
	  this._rectId = 0;
  
	  // The layout return data, which will be populated in getLayout.
	  this._layout = {
		slots: null,
		setWidth: false,
		setHeight: false,
		width: false,
		height: false
	  };
  
	  // Bind sort handlers.
	  this._sortRectsLeftTop = this._sortRectsLeftTop.bind(this);
	  this._sortRectsTopLeft = this._sortRectsTopLeft.bind(this);
	}
  
	/**
	 * @public
	 * @memberof Packer.prototype
	 * @param {Item[]} items
	 * @param {Number} width
	 * @param {Number} height
	 * @param {Number[]} [slots]
	 * @param {Object} [options]
	 * @param {Boolean} [options.fillGaps=false]
	 * @param {Boolean} [options.horizontal=false]
	 * @param {Boolean} [options.alignRight=false]
	 * @param {Boolean} [options.alignBottom=false]
	 * @returns {LayoutData}
	 */
	Packer.prototype.getLayout = function(items, width, height, slots, options) {
	  var layout = this._layout;
	  var fillGaps = !!(options && options.fillGaps);
	  var isHorizontal = !!(options && options.horizontal);
	  var alignRight = !!(options && options.alignRight);
	  var alignBottom = !!(options && options.alignBottom);
	  var rounding = !!(options && options.rounding);
	  var slotSizes = this._slotSizes;
	  var i;
  
	  // Reset layout data.
	  layout.slots = slots ? slots : this._slots;
	  layout.width = isHorizontal ? 0 : rounding ? Math.round(width) : width;
	  layout.height = !isHorizontal ? 0 : rounding ? Math.round(height) : height;
	  layout.setWidth = isHorizontal;
	  layout.setHeight = !isHorizontal;
  
	  // Make sure slots and slot size arrays are reset.
	  layout.slots.length = 0;
	  slotSizes.length = 0;
  
	  // No need to go further if items do not exist.
	  if (!items.length) return layout;
  
	  // Find slots for items.
	  for (i = 0; i < items.length; i++) {
		this._addSlot(items[i], isHorizontal, fillGaps, rounding, alignRight || alignBottom);
	  }
  
	  // If the alignment is set to right we need to adjust the results.
	  if (alignRight) {
		for (i = 0; i < layout.slots.length; i = i + 2) {
		  layout.slots[i] = layout.width - (layout.slots[i] + slotSizes[i]);
		}
	  }
  
	  // If the alignment is set to bottom we need to adjust the results.
	  if (alignBottom) {
		for (i = 1; i < layout.slots.length; i = i + 2) {
		  layout.slots[i] = layout.height - (layout.slots[i] + slotSizes[i]);
		}
	  }
  
	  // Reset slots arrays and rect id.
	  slotSizes.length = 0;
	  this._freeSlots.length = 0;
	  this._newSlots.length = 0;
	  this._rectId = 0;
  
	  return layout;
	};
  
	/**
	 * Calculate position for the layout item. Returns the left and top position
	 * of the item in pixels.
	 *
	 * @private
	 * @memberof Packer.prototype
	 * @param {Item} item
	 * @param {Boolean} isHorizontal
	 * @param {Boolean} fillGaps
	 * @param {Boolean} rounding
	 * @returns {Array}
	 */
	Packer.prototype._addSlot = (function() {
	  var leeway = 0.001;
	  var itemSlot = {};
	  return function(item, isHorizontal, fillGaps, rounding, trackSize) {
		var layout = this._layout;
		var freeSlots = this._freeSlots;
		var newSlots = this._newSlots;
		var rect;
		var rectId;
		var potentialSlots;
		var ignoreCurrentSlots;
		var i;
		var ii;
  
		// Reset new slots.
		newSlots.length = 0;
  
		// Set item slot initial data.
		itemSlot.left = null;
		itemSlot.top = null;
		itemSlot.width = item._width + item._marginLeft + item._marginRight;
		itemSlot.height = item._height + item._marginTop + item._marginBottom;
  
		// Round item slot width and height if needed.
		if (rounding) {
		  itemSlot.width = Math.round(itemSlot.width);
		  itemSlot.height = Math.round(itemSlot.height);
		}
  
		// Try to find a slot for the item.
		for (i = 0; i < freeSlots.length; i++) {
		  rectId = freeSlots[i];
		  if (!rectId) continue;
		  rect = this._getRect(rectId);
		  if (itemSlot.width <= rect.width + leeway && itemSlot.height <= rect.height + leeway) {
			itemSlot.left = rect.left;
			itemSlot.top = rect.top;
			break;
		  }
		}
  
		// If no slot was found for the item.
		if (itemSlot.left === null) {
		  // Position the item in to the bottom left (vertical mode) or top right
		  // (horizontal mode) of the grid.
		  itemSlot.left = !isHorizontal ? 0 : layout.width;
		  itemSlot.top = !isHorizontal ? layout.height : 0;
  
		  // If gaps don't needs filling do not add any current slots to the new
		  // slots array.
		  if (!fillGaps) {
			ignoreCurrentSlots = true;
		  }
		}
  
		// In vertical mode, if the item's bottom overlaps the grid's bottom.
		if (!isHorizontal && itemSlot.top + itemSlot.height > layout.height) {
		  // If item is not aligned to the left edge, create a new slot.
		  if (itemSlot.left > 0) {
			newSlots.push(this._addRect(0, layout.height, itemSlot.left, Infinity));
		  }
  
		  // If item is not aligned to the right edge, create a new slot.
		  if (itemSlot.left + itemSlot.width < layout.width) {
			newSlots.push(
			  this._addRect(
				itemSlot.left + itemSlot.width,
				layout.height,
				layout.width - itemSlot.left - itemSlot.width,
				Infinity
			  )
			);
		  }
  
		  // Update grid height.
		  layout.height = itemSlot.top + itemSlot.height;
		}
  
		// In horizontal mode, if the item's right overlaps the grid's right edge.
		if (isHorizontal && itemSlot.left + itemSlot.width > layout.width) {
		  // If item is not aligned to the top, create a new slot.
		  if (itemSlot.top > 0) {
			newSlots.push(this._addRect(layout.width, 0, Infinity, itemSlot.top));
		  }
  
		  // If item is not aligned to the bottom, create a new slot.
		  if (itemSlot.top + itemSlot.height < layout.height) {
			newSlots.push(
			  this._addRect(
				layout.width,
				itemSlot.top + itemSlot.height,
				Infinity,
				layout.height - itemSlot.top - itemSlot.height
			  )
			);
		  }
  
		  // Update grid width.
		  layout.width = itemSlot.left + itemSlot.width;
		}
  
		// Clean up the current slots making sure there are no old slots that
		// overlap with the item. If an old slot overlaps with the item, split it
		// into smaller slots if necessary.
		for (i = fillGaps ? 0 : ignoreCurrentSlots ? freeSlots.length : i; i < freeSlots.length; i++) {
		  rectId = freeSlots[i];
		  if (!rectId) continue;
		  rect = this._getRect(rectId);
		  potentialSlots = this._splitRect(rect, itemSlot);
		  for (ii = 0; ii < potentialSlots.length; ii++) {
			rectId = potentialSlots[ii];
			rect = this._getRect(rectId);
			// Let's make sure here that we have a big enough slot
			// (width/height > 0.49px) and also let's make sure that the slot is
			// within the boundaries of the grid.
			if (
			  rect.width > 0.49 &&
			  rect.height > 0.49 &&
			  ((!isHorizontal && rect.top < layout.height) ||
				(isHorizontal && rect.left < layout.width))
			) {
			  newSlots.push(rectId);
			}
		  }
		}
  
		// Sanitize new slots.
		if (newSlots.length) {
		  this._purgeRects(newSlots).sort(
			isHorizontal ? this._sortRectsLeftTop : this._sortRectsTopLeft
		  );
		}
  
		// Update layout width/height.
		if (isHorizontal) {
		  layout.width = Math.max(layout.width, itemSlot.left + itemSlot.width);
		} else {
		  layout.height = Math.max(layout.height, itemSlot.top + itemSlot.height);
		}
  
		// Add item slot data to layout slots (and store the slot size for later
		// usage too if necessary).
		layout.slots.push(itemSlot.left, itemSlot.top);
		if (trackSize) this._slotSizes.push(itemSlot.width, itemSlot.height);
  
		// Free/new slots switcheroo!
		this._freeSlots = newSlots;
		this._newSlots = freeSlots;
	  };
	})();
  
	/**
	 * Add a new rectangle to the rectangle store. Returns the id of the new
	 * rectangle.
	 *
	 * @private
	 * @memberof Packer.prototype
	 * @param {Number} left
	 * @param {Number} top
	 * @param {Number} width
	 * @param {Number} height
	 * @returns {RectId}
	 */
	Packer.prototype._addRect = function(left, top, width, height) {
	  var rectId = ++this._rectId;
	  var rectStore = this._rectStore;
  
	  rectStore[rectId] = left || 0;
	  rectStore[++this._rectId] = top || 0;
	  rectStore[++this._rectId] = width || 0;
	  rectStore[++this._rectId] = height || 0;
  
	  return rectId;
	};
  
	/**
	 * Get rectangle data from the rectangle store by id. Optionally you can
	 * provide a target object where the rectangle data will be written in. By
	 * default an internal object is reused as a target object.
	 *
	 * @private
	 * @memberof Packer.prototype
	 * @param {RectId} id
	 * @param {Object} [target]
	 * @returns {Object}
	 */
	Packer.prototype._getRect = function(id, target) {
	  var rectItem = target ? target : this._rectItem;
	  var rectStore = this._rectStore;
  
	  rectItem.left = rectStore[id] || 0;
	  rectItem.top = rectStore[++id] || 0;
	  rectItem.width = rectStore[++id] || 0;
	  rectItem.height = rectStore[++id] || 0;
  
	  return rectItem;
	};
  
	/**
	 * Punch a hole into a rectangle and split the remaining area into smaller
	 * rectangles (4 at max).
	 *
	 * @private
	 * @memberof Packer.prototype
	 * @param {Rectangle} rect
	 * @param {Rectangle} hole
	 * @returns {RectId[]}
	 */
	Packer.prototype._splitRect = (function() {
	  var results = [];
	  return function(rect, hole) {
		// Reset old results.
		results.length = 0;
  
		// If the rect does not overlap with the hole add rect to the return data
		// as is.
		if (!this._doRectsOverlap(rect, hole)) {
		  results.push(this._addRect(rect.left, rect.top, rect.width, rect.height));
		  return results;
		}
  
		// Left split.
		if (rect.left < hole.left) {
		  results.push(this._addRect(rect.left, rect.top, hole.left - rect.left, rect.height));
		}
  
		// Right split.
		if (rect.left + rect.width > hole.left + hole.width) {
		  results.push(
			this._addRect(
			  hole.left + hole.width,
			  rect.top,
			  rect.left + rect.width - (hole.left + hole.width),
			  rect.height
			)
		  );
		}
  
		// Top split.
		if (rect.top < hole.top) {
		  results.push(this._addRect(rect.left, rect.top, rect.width, hole.top - rect.top));
		}
  
		// Bottom split.
		if (rect.top + rect.height > hole.top + hole.height) {
		  results.push(
			this._addRect(
			  rect.left,
			  hole.top + hole.height,
			  rect.width,
			  rect.top + rect.height - (hole.top + hole.height)
			)
		  );
		}
  
		return results;
	  };
	})();
  
	/**
	 * Check if two rectangles overlap.
	 *
	 * @private
	 * @memberof Packer.prototype
	 * @param {Rectangle} a
	 * @param {Rectangle} b
	 * @returns {Boolean}
	 */
	Packer.prototype._doRectsOverlap = function(a, b) {
	  return !(
		a.left + a.width <= b.left ||
		b.left + b.width <= a.left ||
		a.top + a.height <= b.top ||
		b.top + b.height <= a.top
	  );
	};
  
	/**
	 * Check if a rectangle is fully within another rectangle.
	 *
	 * @private
	 * @memberof Packer.prototype
	 * @param {Rectangle} a
	 * @param {Rectangle} b
	 * @returns {Boolean}
	 */
	Packer.prototype._isRectWithinRect = function(a, b) {
	  return (
		a.left >= b.left &&
		a.top >= b.top &&
		a.left + a.width <= b.left + b.width &&
		a.top + a.height <= b.top + b.height
	  );
	};
  
	/**
	 * Loops through an array of rectangle ids and resets all that are fully
	 * within another rectangle in the array. Resetting in this case means that
	 * the rectangle id value is replaced with zero.
	 *
	 * @private
	 * @memberof Packer.prototype
	 * @param {RectId[]} rectIds
	 * @returns {RectId[]}
	 */
	Packer.prototype._purgeRects = (function() {
	  var rectA = {};
	  var rectB = {};
	  return function(rectIds) {
		var i = rectIds.length;
		var ii;
  
		while (i--) {
		  ii = rectIds.length;
		  if (!rectIds[i]) continue;
		  this._getRect(rectIds[i], rectA);
		  while (ii--) {
			if (!rectIds[ii] || i === ii) continue;
			if (this._isRectWithinRect(rectA, this._getRect(rectIds[ii], rectB))) {
			  rectIds[i] = 0;
			  break;
			}
		  }
		}
  
		return rectIds;
	  };
	})();
  
	/**
	 * Sort rectangles with top-left gravity.
	 *
	 * @private
	 * @memberof Packer.prototype
	 * @param {RectId} aId
	 * @param {RectId} bId
	 * @returns {Number}
	 */
	Packer.prototype._sortRectsTopLeft = (function() {
	  var rectA = {};
	  var rectB = {};
	  return function(aId, bId) {
		this._getRect(aId, rectA);
		this._getRect(bId, rectB);
		// prettier-ignore
		return rectA.top < rectB.top ? -1 :
			   rectA.top > rectB.top ? 1 :
			   rectA.left < rectB.left ? -1 :
			   rectA.left > rectB.left ? 1 : 0;
	  };
	})();
  
	/**
	 * Sort rectangles with left-top gravity.
	 *
	 * @private
	 * @memberof Packer.prototype
	 * @param {RectId} aId
	 * @param {RectId} bId
	 * @returns {Number}
	 */
	Packer.prototype._sortRectsLeftTop = (function() {
	  var rectA = {};
	  var rectB = {};
	  return function(aId, bId) {
		this._getRect(aId, rectA);
		this._getRect(bId, rectB);
		// prettier-ignore
		return rectA.left < rectB.left ? -1 :
			   rectA.left > rectB.left ? 1 :
			   rectA.top < rectB.top ? -1 :
			   rectA.top > rectB.top ? 1 : 0;
	  };
	})();
  
	var htmlCollectionType = '[object HTMLCollection]';
	var nodeListType = '[object NodeList]';
  
	/**
	 * Check if a value is a node list
	 *
	 * @param {*} val
	 * @returns {Boolean}
	 */
	function isNodeList(val) {
	  var type = Object.prototype.toString.call(val);
	  return type === htmlCollectionType || type === nodeListType;
	}
  
	/**
	 * Converts a value to an array or clones an array.
	 *
	 * @param {*} target
	 * @returns {Array}
	 */
	function toArray(target) {
	  return isNodeList(target) ? Array.prototype.slice.call(target) : Array.prototype.concat(target);
	}
  
	var packer = new Packer();
	var noop = function() {};
  
	/**
	 * Creates a new Grid instance.
	 *
	 * @class
	 * @param {(HTMLElement|String)} element
	 * @param {Object} [options]
	 * @param {(?HTMLElement[]|NodeList|String)} [options.items]
	 * @param {Number} [options.showDuration=300]
	 * @param {String} [options.showEasing="ease"]
	 * @param {Object} [options.visibleStyles]
	 * @param {Number} [options.hideDuration=300]
	 * @param {String} [options.hideEasing="ease"]
	 * @param {Object} [options.hiddenStyles]
	 * @param {(Function|Object)} [options.layout]
	 * @param {Boolean} [options.layout.fillGaps=false]
	 * @param {Boolean} [options.layout.horizontal=false]
	 * @param {Boolean} [options.layout.alignRight=false]
	 * @param {Boolean} [options.layout.alignBottom=false]
	 * @param {Boolean} [options.layout.rounding=true]
	 * @param {(Boolean|Number)} [options.layoutOnResize=100]
	 * @param {Boolean} [options.layoutOnInit=true]
	 * @param {Number} [options.layoutDuration=300]
	 * @param {String} [options.layoutEasing="ease"]
	 * @param {?Object} [options.sortData=null]
	 * @param {Boolean} [options.dragEnabled=false]
	 * @param {?HtmlElement} [options.dragContainer=null]
	 * @param {?Function} [options.dragStartPredicate]
	 * @param {Number} [options.dragStartPredicate.distance=0]
	 * @param {Number} [options.dragStartPredicate.delay=0]
	 * @param {(Boolean|String)} [options.dragStartPredicate.handle=false]
	 * @param {?String} [options.dragAxis]
	 * @param {(Boolean|Function)} [options.dragSort=true]
	 * @param {Number} [options.dragSortInterval=100]
	 * @param {(Function|Object)} [options.dragSortPredicate]
	 * @param {Number} [options.dragSortPredicate.threshold=50]
	 * @param {String} [options.dragSortPredicate.action="move"]
	 * @param {Number} [options.dragReleaseDuration=300]
	 * @param {String} [options.dragReleaseEasing="ease"]
	 * @param {Object} [options.dragHammerSettings={touchAction: "none"}]
	 * @param {String} [options.containerClass="muuri"]
	 * @param {String} [options.itemClass="muuri-item"]
	 * @param {String} [options.itemVisibleClass="muuri-item-visible"]
	 * @param {String} [options.itemHiddenClass="muuri-item-hidden"]
	 * @param {String} [options.itemPositioningClass="muuri-item-positioning"]
	 * @param {String} [options.itemDraggingClass="muuri-item-dragging"]
	 * @param {String} [options.itemReleasingClass="muuri-item-releasing"]
	 */
	function Grid(element, options) {
	  var inst = this;
	  var settings;
	  var items;
	  var layoutOnResize;
  
	  // Allow passing element as selector string. Store element for instance.
	  element = this._element = typeof element === 'string' ? document.querySelector(element) : element;
  
	  // Throw an error if the container element is not body element or does not
	  // exist within the body element.
	  if (!document.body.contains(element)) {
		throw new Error('Container element must be an existing DOM element');
	  }
  
	  // Create instance settings by merging the options with default options.
	  settings = this._settings = mergeSettings(Grid.defaultOptions, options);
  
	  // Sanitize dragSort setting.
	  if (typeof settings.dragSort !== 'function') {
		settings.dragSort = !!settings.dragSort;
	  }
  
	  // Create instance id and store it to the grid instances collection.
	  this._id = createUid();
	  gridInstances[this._id] = inst;
  
	  // Destroyed flag.
	  this._isDestroyed = false;
  
	  // The layout object (mutated on every layout).
	  this._layout = {
		id: 0,
		items: [],
		slots: [],
		setWidth: false,
		setHeight: false,
		width: 0,
		height: 0
	  };
  
	  // Create private Emitter instance.
	  this._emitter = new Emitter();
  
	  // Add container element's class name.
	  addClass(element, settings.containerClass);
  
	  // Create initial items.
	  this._items = [];
	  items = settings.items;
	  if (typeof items === 'string') {
		toArray(element.children).forEach(function(itemElement) {
		  if (items === '*' || elementMatches(itemElement, items)) {
			inst._items.push(new Item(inst, itemElement));
		  }
		});
	  } else if (Array.isArray(items) || isNodeList(items)) {
		this._items = toArray(items).map(function(itemElement) {
		  return new Item(inst, itemElement);
		});
	  }
  
	  // If layoutOnResize option is a valid number sanitize it and bind the resize
	  // handler.
	  layoutOnResize = settings.layoutOnResize;
	  if (typeof layoutOnResize !== 'number') {
		layoutOnResize = layoutOnResize === true ? 0 : -1;
	  }
	  if (layoutOnResize >= 0) {
		window.addEventListener(
		  'resize',
		  (inst._resizeHandler = debounce(function() {
			inst.refreshItems().layout();
		  }, layoutOnResize))
		);
	  }
  
	  // Layout on init if necessary.
	  if (settings.layoutOnInit) {
		this.layout(true);
	  }
	}
  
	/**
	 * Public properties
	 * *****************
	 */
  
	/**
	 * @see Item
	 */
	Grid.Item = Item;
  
	/**
	 * @see ItemLayout
	 */
	Grid.ItemLayout = ItemLayout;
  
	/**
	 * @see ItemVisibility
	 */
	Grid.ItemVisibility = ItemVisibility;
  
	/**
	 * @see ItemRelease
	 */
	Grid.ItemRelease = ItemRelease;
  
	/**
	 * @see ItemMigrate
	 */
	Grid.ItemMigrate = ItemMigrate;
  
	/**
	 * @see ItemAnimate
	 */
	Grid.ItemAnimate = ItemAnimate;
  
	/**
	 * @see ItemDrag
	 */
	Grid.ItemDrag = ItemDrag;
  
	/**
	 * @see Emitter
	 */
	Grid.Emitter = Emitter;
  
	/**
	 * Default options for Grid instance.
	 *
	 * @public
	 * @memberof Grid
	 */
	Grid.defaultOptions = {
	  // Item elements
	  items: '*',
  
	  // Default show animation
	  showDuration: 300,
	  showEasing: 'ease',
  
	  // Default hide animation
	  hideDuration: 300,
	  hideEasing: 'ease',
  
	  // Item's visible/hidden state styles
	  visibleStyles: {
		opacity: '1',
		transform: 'scale(1)'
	  },
	  hiddenStyles: {
		opacity: '0',
		transform: 'scale(0.5)'
	  },
  
	  // Layout
	  layout: {
		fillGaps: false,
		horizontal: false,
		alignRight: false,
		alignBottom: false,
		rounding: true
	  },
	  layoutOnResize: 100,
	  layoutOnInit: true,
	  layoutDuration: 300,
	  layoutEasing: 'ease',
  
	  // Sorting
	  sortData: null,
  
	  // Drag & Drop
	  dragEnabled: false,
	  dragContainer: null,
	  dragStartPredicate: {
		distance: 0,
		delay: 0,
		handle: false
	  },
	  dragAxis: null,
	  dragSort: true,
	  dragSortInterval: 100,
	  dragSortPredicate: {
		threshold: 50,
		action: 'move'
	  },
	  dragReleaseDuration: 300,
	  dragReleaseEasing: 'ease',
	  dragHammerSettings: {
		touchAction: 'none'
	  },
  
	  // Classnames
	  containerClass: 'muuri',
	  itemClass: 'muuri-item',
	  itemVisibleClass: 'muuri-item-shown',
	  itemHiddenClass: 'muuri-item-hidden',
	  itemPositioningClass: 'muuri-item-positioning',
	  itemDraggingClass: 'muuri-item-dragging',
	  itemReleasingClass: 'muuri-item-releasing'
	};
  
	/**
	 * Public prototype methods
	 * ************************
	 */
  
	/**
	 * Bind an event listener.
	 *
	 * @public
	 * @memberof Grid.prototype
	 * @param {String} event
	 * @param {Function} listener
	 * @returns {Grid}
	 */
	Grid.prototype.on = function(event, listener) {
	  this._emitter.on(event, listener);
	  return this;
	};
  
	/**
	 * Bind an event listener that is triggered only once.
	 *
	 * @public
	 * @memberof Grid.prototype
	 * @param {String} event
	 * @param {Function} listener
	 * @returns {Grid}
	 */
	Grid.prototype.once = function(event, listener) {
	  this._emitter.once(event, listener);
	  return this;
	};
  
	/**
	 * Unbind an event listener.
	 *
	 * @public
	 * @memberof Grid.prototype
	 * @param {String} event
	 * @param {Function} listener
	 * @returns {Grid}
	 */
	Grid.prototype.off = function(event, listener) {
	  this._emitter.off(event, listener);
	  return this;
	};
  
	/**
	 * Get the container element.
	 *
	 * @public
	 * @memberof Grid.prototype
	 * @returns {HTMLElement}
	 */
	Grid.prototype.getElement = function() {
	  return this._element;
	};
  
	/**
	 * Get all items. Optionally you can provide specific targets (elements and
	 * indices). Note that the returned array is not the same object used by the
	 * instance so modifying it will not affect instance's items. All items that
	 * are not found are omitted from the returned array.
	 *
	 * @public
	 * @memberof Grid.prototype
	 * @param {GridMultiItemQuery} [targets]
	 * @returns {Item[]}
	 */
	Grid.prototype.getItems = function(targets) {
	  // Return all items immediately if no targets were provided or if the
	  // instance is destroyed.
	  if (this._isDestroyed || (!targets && targets !== 0)) {
		return this._items.slice(0);
	  }
  
	  var ret = [];
	  var targetItems = toArray(targets);
	  var item;
	  var i;
  
	  // If target items are defined return filtered results.
	  for (i = 0; i < targetItems.length; i++) {
		item = this._getItem(targetItems[i]);
		item && ret.push(item);
	  }
  
	  return ret;
	};
  
	/**
	 * Update the cached dimensions of the instance's items.
	 *
	 * @public
	 * @memberof Grid.prototype
	 * @param {GridMultiItemQuery} [items]
	 * @returns {Grid}
	 */
	Grid.prototype.refreshItems = function(items) {
	  if (this._isDestroyed) return this;
  
	  var targets = this.getItems(items);
	  var i;
  
	  for (i = 0; i < targets.length; i++) {
		targets[i]._refreshDimensions();
	  }
  
	  return this;
	};
  
	/**
	 * Update the sort data of the instance's items.
	 *
	 * @public
	 * @memberof Grid.prototype
	 * @param {GridMultiItemQuery} [items]
	 * @returns {Grid}
	 */
	Grid.prototype.refreshSortData = function(items) {
	  if (this._isDestroyed) return this;
  
	  var targetItems = this.getItems(items);
	  var i;
  
	  for (i = 0; i < targetItems.length; i++) {
		targetItems[i]._refreshSortData();
	  }
  
	  return this;
	};
  
	/**
	 * Synchronize the item elements to match the order of the items in the DOM.
	 * This comes handy if you need to keep the DOM structure matched with the
	 * order of the items. Note that if an item's element is not currently a child
	 * of the container element (if it is dragged for example) it is ignored and
	 * left untouched.
	 *
	 * @public
	 * @memberof Grid.prototype
	 * @returns {Grid}
	 */
	Grid.prototype.synchronize = function() {
	  if (this._isDestroyed) return this;
  
	  var container = this._element;
	  var items = this._items;
	  var fragment;
	  var element;
	  var i;
  
	  // Append all elements in order to the container element.
	  if (items.length) {
		for (i = 0; i < items.length; i++) {
		  element = items[i]._element;
		  if (element.parentNode === container) {
			fragment = fragment || document.createDocumentFragment();
			fragment.appendChild(element);
		  }
		}
  
		if (fragment) container.appendChild(fragment);
	  }
  
	  // Emit synchronize event.
	  this._emit(eventSynchronize);
  
	  return this;
	};
  
	/**
	 * Calculate and apply item positions.
	 *
	 * @public
	 * @memberof Grid.prototype
	 * @param {Boolean} [instant=false]
	 * @param {LayoutCallback} [onFinish]
	 * @returns {Grid}
	 */
	Grid.prototype.layout = function(instant, onFinish) {
	  if (this._isDestroyed) return this;
  
	  var inst = this;
	  var element = this._element;
	  var layout = this._updateLayout();
	  var layoutId = layout.id;
	  var itemsLength = layout.items.length;
	  var counter = itemsLength;
	  var callback = typeof instant === 'function' ? instant : onFinish;
	  var isCallbackFunction = typeof callback === 'function';
	  var callbackItems = isCallbackFunction ? layout.items.slice(0) : null;
	  var isBorderBox;
	  var item;
	  var i;
  
	  // The finish function, which will be used for checking if all the items
	  // have laid out yet. After all items have finished their animations call
	  // callback and emit layoutEnd event. Only emit layoutEnd event if there
	  // hasn't been a new layout call during this layout.
	  function tryFinish() {
		if (--counter > 0) return;
		var hasLayoutChanged = inst._layout.id !== layoutId;
		isCallbackFunction && callback(hasLayoutChanged, callbackItems);
		if (!hasLayoutChanged && inst._hasListeners(eventLayoutEnd)) {
		  inst._emit(eventLayoutEnd, layout.items.slice(0));
		}
	  }
  
	  // If grid's width or height was modified, we need to update it's cached
	  // dimensions. Also keep in mind that grid's cached width/height should
	  // always equal to what elem.getBoundingClientRect() would return, so
	  // therefore we need to add the grid element's borders to the dimensions if
	  // it's box-sizing is border-box.
	  if (
		(layout.setHeight && typeof layout.height === 'number') ||
		(layout.setWidth && typeof layout.width === 'number')
	  ) {
		isBorderBox = getStyle(element, 'box-sizing') === 'border-box';
	  }
	  if (layout.setHeight) {
		if (typeof layout.height === 'number') {
		  element.style.height =
			(isBorderBox ? layout.height + this._borderTop + this._borderBottom : layout.height) + 'px';
		} else {
		  element.style.height = layout.height;
		}
	  }
	  if (layout.setWidth) {
		if (typeof layout.width === 'number') {
		  element.style.width =
			(isBorderBox ? layout.width + this._borderLeft + this._borderRight : layout.width) + 'px';
		} else {
		  element.style.width = layout.width;
		}
	  }
  
	  // Emit layoutStart event. Note that this is intentionally emitted after the
	  // container element's dimensions are set, because otherwise there would be
	  // no hook for reacting to container dimension changes.
	  if (this._hasListeners(eventLayoutStart)) {
		this._emit(eventLayoutStart, layout.items.slice(0));
	  }
  
	  // If there are no items let's finish quickly.
	  if (!itemsLength) {
		tryFinish();
		return this;
	  }
  
	  // If there are items let's position them.
	  for (i = 0; i < itemsLength; i++) {
		item = layout.items[i];
		if (!item) continue;
  
		// Update item's position.
		item._left = layout.slots[i * 2];
		item._top = layout.slots[i * 2 + 1];
  
		// Layout item if it is not dragged.
		item.isDragging() ? tryFinish() : item._layout.start(instant === true, tryFinish);
	  }
  
	  return this;
	};
  
	/**
	 * Add new items by providing the elements you wish to add to the instance and
	 * optionally provide the index where you want the items to be inserted into.
	 * All elements that are not already children of the container element will be
	 * automatically appended to the container element. If an element has it's CSS
	 * display property set to "none" it will be marked as inactive during the
	 * initiation process. As long as the item is inactive it will not be part of
	 * the layout, but it will retain it's index. You can activate items at any
	 * point with grid.show() method. This method will automatically call
	 * grid.layout() if one or more of the added elements are visible. If only
	 * hidden items are added no layout will be called. All the new visible items
	 * are positioned without animation during their first layout.
	 *
	 * @public
	 * @memberof Grid.prototype
	 * @param {(HTMLElement|HTMLElement[])} elements
	 * @param {Object} [options]
	 * @param {Number} [options.index=-1]
	 * @param {Boolean} [options.isActive]
	 * @param {(Boolean|LayoutCallback|String)} [options.layout=true]
	 * @returns {Item[]}
	 */
	Grid.prototype.add = function(elements, options) {
	  if (this._isDestroyed || !elements) return [];
  
	  var newItems = toArray(elements);
	  if (!newItems.length) return newItems;
  
	  var opts = options || 0;
	  var layout = opts.layout ? opts.layout : opts.layout === undefined;
	  var items = this._items;
	  var needsLayout = false;
	  var item;
	  var i;
  
	  // Map provided elements into new grid items.
	  for (i = 0; i < newItems.length; i++) {
		item = new Item(this, newItems[i], opts.isActive);
		newItems[i] = item;
  
		// If the item to be added is active, we need to do a layout. Also, we
		// need to mark the item with the skipNextAnimation flag to make it
		// position instantly (without animation) during the next layout. Without
		// the hack the item would animate to it's new position from the northwest
		// corner of the grid, which feels a bit buggy (imho).
		if (item._isActive) {
		  needsLayout = true;
		  item._layout._skipNextAnimation = true;
		}
	  }
  
	  // Add the new items to the items collection to correct index.
	  arrayInsert(items, newItems, opts.index);
  
	  // Emit add event.
	  if (this._hasListeners(eventAdd)) {
		this._emit(eventAdd, newItems.slice(0));
	  }
  
	  // If layout is needed.
	  if (needsLayout && layout) {
		this.layout(layout === 'instant', typeof layout === 'function' ? layout : undefined);
	  }
  
	  return newItems;
	};
  
	/**
	 * Remove items from the instance.
	 *
	 * @public
	 * @memberof Grid.prototype
	 * @param {GridMultiItemQuery} items
	 * @param {Object} [options]
	 * @param {Boolean} [options.removeElements=false]
	 * @param {(Boolean|LayoutCallback|String)} [options.layout=true]
	 * @returns {Item[]}
	 */
	Grid.prototype.remove = function(items, options) {
	  if (this._isDestroyed) return this;
  
	  var opts = options || 0;
	  var layout = opts.layout ? opts.layout : opts.layout === undefined;
	  var needsLayout = false;
	  var allItems = this.getItems();
	  var targetItems = this.getItems(items);
	  var indices = [];
	  var item;
	  var i;
  
	  // Remove the individual items.
	  for (i = 0; i < targetItems.length; i++) {
		item = targetItems[i];
		indices.push(allItems.indexOf(item));
		if (item._isActive) needsLayout = true;
		item._destroy(opts.removeElements);
	  }
  
	  // Emit remove event.
	  if (this._hasListeners(eventRemove)) {
		this._emit(eventRemove, targetItems.slice(0), indices);
	  }
  
	  // If layout is needed.
	  if (needsLayout && layout) {
		this.layout(layout === 'instant', typeof layout === 'function' ? layout : undefined);
	  }
  
	  return targetItems;
	};
  
	/**
	 * Show instance items.
	 *
	 * @public
	 * @memberof Grid.prototype
	 * @param {GridMultiItemQuery} items
	 * @param {Object} [options]
	 * @param {Boolean} [options.instant=false]
	 * @param {ShowCallback} [options.onFinish]
	 * @param {(Boolean|LayoutCallback|String)} [options.layout=true]
	 * @returns {Grid}
	 */
	Grid.prototype.show = function(items, options) {
	  if (this._isDestroyed) return this;
	  this._setItemsVisibility(items, true, options);
	  return this;
	};
  
	/**
	 * Hide instance items.
	 *
	 * @public
	 * @memberof Grid.prototype
	 * @param {GridMultiItemQuery} items
	 * @param {Object} [options]
	 * @param {Boolean} [options.instant=false]
	 * @param {HideCallback} [options.onFinish]
	 * @param {(Boolean|LayoutCallback|String)} [options.layout=true]
	 * @returns {Grid}
	 */
	Grid.prototype.hide = function(items, options) {
	  if (this._isDestroyed) return this;
	  this._setItemsVisibility(items, false, options);
	  return this;
	};
  
	/**
	 * Filter items. Expects at least one argument, a predicate, which should be
	 * either a function or a string. The predicate callback is executed for every
	 * item in the instance. If the return value of the predicate is truthy the
	 * item in question will be shown and otherwise hidden. The predicate callback
	 * receives the item instance as it's argument. If the predicate is a string
	 * it is considered to be a selector and it is checked against every item
	 * element in the instance with the native element.matches() method. All the
	 * matching items will be shown and others hidden.
	 *
	 * @public
	 * @memberof Grid.prototype
	 * @param {(Function|String)} predicate
	 * @param {Object} [options]
	 * @param {Boolean} [options.instant=false]
	 * @param {FilterCallback} [options.onFinish]
	 * @param {(Boolean|LayoutCallback|String)} [options.layout=true]
	 * @returns {Grid}
	 */
	Grid.prototype.filter = function(predicate, options) {
	  if (this._isDestroyed || !this._items.length) return this;
  
	  var itemsToShow = [];
	  var itemsToHide = [];
	  var isPredicateString = typeof predicate === 'string';
	  var isPredicateFn = typeof predicate === 'function';
	  var opts = options || 0;
	  var isInstant = opts.instant === true;
	  var layout = opts.layout ? opts.layout : opts.layout === undefined;
	  var onFinish = typeof opts.onFinish === 'function' ? opts.onFinish : null;
	  var tryFinishCounter = -1;
	  var tryFinish = noop;
	  var item;
	  var i;
  
	  // If we have onFinish callback, let's create proper tryFinish callback.
	  if (onFinish) {
		tryFinish = function() {
		  ++tryFinishCounter && onFinish(itemsToShow.slice(0), itemsToHide.slice(0));
		};
	  }
  
	  // Check which items need to be shown and which hidden.
	  if (isPredicateFn || isPredicateString) {
		for (i = 0; i < this._items.length; i++) {
		  item = this._items[i];
		  if (isPredicateFn ? predicate(item) : elementMatches(item._element, predicate)) {
			itemsToShow.push(item);
		  } else {
			itemsToHide.push(item);
		  }
		}
	  }
  
	  // Show items that need to be shown.
	  if (itemsToShow.length) {
		this.show(itemsToShow, {
		  instant: isInstant,
		  onFinish: tryFinish,
		  layout: false
		});
	  } else {
		tryFinish();
	  }
  
	  // Hide items that need to be hidden.
	  if (itemsToHide.length) {
		this.hide(itemsToHide, {
		  instant: isInstant,
		  onFinish: tryFinish,
		  layout: false
		});
	  } else {
		tryFinish();
	  }
  
	  // If there are any items to filter.
	  if (itemsToShow.length || itemsToHide.length) {
		// Emit filter event.
		if (this._hasListeners(eventFilter)) {
		  this._emit(eventFilter, itemsToShow.slice(0), itemsToHide.slice(0));
		}
  
		// If layout is needed.
		if (layout) {
		  this.layout(layout === 'instant', typeof layout === 'function' ? layout : undefined);
		}
	  }
  
	  return this;
	};
  
	/**
	 * Sort items. There are three ways to sort the items. The first is simply by
	 * providing a function as the comparer which works identically to native
	 * array sort. Alternatively you can sort by the sort data you have provided
	 * in the instance's options. Just provide the sort data key(s) as a string
	 * (separated by space) and the items will be sorted based on the provided
	 * sort data keys. Lastly you have the opportunity to provide a presorted
	 * array of items which will be used to sync the internal items array in the
	 * same order.
	 *
	 * @public
	 * @memberof Grid.prototype
	 * @param {(Function|Item[]|String|String[])} comparer
	 * @param {Object} [options]
	 * @param {Boolean} [options.descending=false]
	 * @param {(Boolean|LayoutCallback|String)} [options.layout=true]
	 * @returns {Grid}
	 */
	Grid.prototype.sort = (function() {
	  var sortComparer;
	  var isDescending;
	  var origItems;
	  var indexMap;
  
	  function parseCriteria(data) {
		return data
		  .trim()
		  .split(' ')
		  .map(function(val) {
			return val.split(':');
		  });
	  }
  
	  function getIndexMap(items) {
		var ret = {};
		for (var i = 0; i < items.length; i++) {
		  ret[items[i]._id] = i;
		}
		return ret;
	  }
  
	  function compareIndices(itemA, itemB) {
		var indexA = indexMap[itemA._id];
		var indexB = indexMap[itemB._id];
		return isDescending ? indexB - indexA : indexA - indexB;
	  }
  
	  function defaultComparer(a, b) {
		var result = 0;
		var criteriaName;
		var criteriaOrder;
		var valA;
		var valB;
  
		// Loop through the list of sort criteria.
		for (var i = 0; i < sortComparer.length; i++) {
		  // Get the criteria name, which should match an item's sort data key.
		  criteriaName = sortComparer[i][0];
		  criteriaOrder = sortComparer[i][1];
  
		  // Get items' cached sort values for the criteria. If the item has no sort
		  // data let's update the items sort data (this is a lazy load mechanism).
		  valA = (a._sortData ? a : a._refreshSortData())._sortData[criteriaName];
		  valB = (b._sortData ? b : b._refreshSortData())._sortData[criteriaName];
  
		  // Sort the items in descending order if defined so explicitly. Otherwise
		  // sort items in ascending order.
		  if (criteriaOrder === 'desc' || (!criteriaOrder && isDescending)) {
			result = valB < valA ? -1 : valB > valA ? 1 : 0;
		  } else {
			result = valA < valB ? -1 : valA > valB ? 1 : 0;
		  }
  
		  // If we have -1 or 1 as the return value, let's return it immediately.
		  if (result) return result;
		}
  
		// If values are equal let's compare the item indices to make sure we
		// have a stable sort.
		if (!result) {
		  if (!indexMap) indexMap = getIndexMap(origItems);
		  result = compareIndices(a, b);
		}
		return result;
	  }
  
	  function customComparer(a, b) {
		var result = sortComparer(a, b);
		// If descending let's invert the result value.
		if (isDescending && result) result = -result;
		// If we have a valid result (not zero) let's return it right away.
		if (result) return result;
		// If result is zero let's compare the item indices to make sure we have a
		// stable sort.
		if (!indexMap) indexMap = getIndexMap(origItems);
		return compareIndices(a, b);
	  }
  
	  return function(comparer, options) {
		if (this._isDestroyed || this._items.length < 2) return this;
  
		var items = this._items;
		var opts = options || 0;
		var layout = opts.layout ? opts.layout : opts.layout === undefined;
		var i;
  
		// Setup parent scope data.
		sortComparer = comparer;
		isDescending = !!opts.descending;
		origItems = items.slice(0);
		indexMap = null;
  
		// If function is provided do a native array sort.
		if (typeof sortComparer === 'function') {
		  items.sort(customComparer);
		}
		// Otherwise if we got a string, let's sort by the sort data as provided in
		// the instance's options.
		else if (typeof sortComparer === 'string') {
		  sortComparer = parseCriteria(comparer);
		  items.sort(defaultComparer);
		}
		// Otherwise if we got an array, let's assume it's a presorted array of the
		// items and order the items based on it.
		else if (Array.isArray(sortComparer)) {
		  if (sortComparer.length !== items.length) {
			throw new Error('[' + namespace + '] sort reference items do not match with grid items.');
		  }
		  for (i = 0; i < items.length; i++) {
			if (sortComparer.indexOf(items[i]) < 0) {
			  throw new Error('[' + namespace + '] sort reference items do not match with grid items.');
			}
			items[i] = sortComparer[i];
		  }
		  if (isDescending) items.reverse();
		}
		// Otherwise let's just skip it, nothing we can do here.
		else {
		  /** @todo Maybe throw an error here? */
		  return this;
		}
  
		// Emit sort event.
		if (this._hasListeners(eventSort)) {
		  this._emit(eventSort, items.slice(0), origItems);
		}
  
		// If layout is needed.
		if (layout) {
		  this.layout(layout === 'instant', typeof layout === 'function' ? layout : undefined);
		}
  
		return this;
	  };
	})();
  
	/**
	 * Move item to another index or in place of another item.
	 *
	 * @public
	 * @memberof Grid.prototype
	 * @param {GridSingleItemQuery} item
	 * @param {GridSingleItemQuery} position
	 * @param {Object} [options]
	 * @param {String} [options.action="move"]
	 *   - Accepts either "move" or "swap".
	 *   - "move" moves the item in place of the other item.
	 *   - "swap" swaps the position of the items.
	 * @param {(Boolean|LayoutCallback|String)} [options.layout=true]
	 * @returns {Grid}
	 */
	Grid.prototype.move = function(item, position, options) {
	  if (this._isDestroyed || this._items.length < 2) return this;
  
	  var items = this._items;
	  var opts = options || 0;
	  var layout = opts.layout ? opts.layout : opts.layout === undefined;
	  var isSwap = opts.action === 'swap';
	  var action = isSwap ? 'swap' : 'move';
	  var fromItem = this._getItem(item);
	  var toItem = this._getItem(position);
	  var fromIndex;
	  var toIndex;
  
	  // Make sure the items exist and are not the same.
	  if (fromItem && toItem && fromItem !== toItem) {
		// Get the indices of the items.
		fromIndex = items.indexOf(fromItem);
		toIndex = items.indexOf(toItem);
  
		// Do the move/swap.
		if (isSwap) {
		  arraySwap(items, fromIndex, toIndex);
		} else {
		  arrayMove(items, fromIndex, toIndex);
		}
  
		// Emit move event.
		if (this._hasListeners(eventMove)) {
		  this._emit(eventMove, {
			item: fromItem,
			fromIndex: fromIndex,
			toIndex: toIndex,
			action: action
		  });
		}
  
		// If layout is needed.
		if (layout) {
		  this.layout(layout === 'instant', typeof layout === 'function' ? layout : undefined);
		}
	  }
  
	  return this;
	};
  
	/**
	 * Send item to another Grid instance.
	 *
	 * @public
	 * @memberof Grid.prototype
	 * @param {GridSingleItemQuery} item
	 * @param {Grid} grid
	 * @param {GridSingleItemQuery} position
	 * @param {Object} [options]
	 * @param {HTMLElement} [options.appendTo=document.body]
	 * @param {(Boolean|LayoutCallback|String)} [options.layoutSender=true]
	 * @param {(Boolean|LayoutCallback|String)} [options.layoutReceiver=true]
	 * @returns {Grid}
	 */
	Grid.prototype.send = function(item, grid, position, options) {
	  if (this._isDestroyed || grid._isDestroyed || this === grid) return this;
  
	  // Make sure we have a valid target item.
	  item = this._getItem(item);
	  if (!item) return this;
  
	  var opts = options || 0;
	  var container = opts.appendTo || document.body;
	  var layoutSender = opts.layoutSender ? opts.layoutSender : opts.layoutSender === undefined;
	  var layoutReceiver = opts.layoutReceiver
		? opts.layoutReceiver
		: opts.layoutReceiver === undefined;
  
	  // Start the migration process.
	  item._migrate.start(grid, position, container);
  
	  // If migration was started successfully and the item is active, let's layout
	  // the grids.
	  if (item._migrate._isActive && item._isActive) {
		if (layoutSender) {
		  this.layout(
			layoutSender === 'instant',
			typeof layoutSender === 'function' ? layoutSender : undefined
		  );
		}
		if (layoutReceiver) {
		  grid.layout(
			layoutReceiver === 'instant',
			typeof layoutReceiver === 'function' ? layoutReceiver : undefined
		  );
		}
	  }
  
	  return this;
	};
  
	/**
	 * Destroy the instance.
	 *
	 * @public
	 * @memberof Grid.prototype
	 * @param {Boolean} [removeElements=false]
	 * @returns {Grid}
	 */
	Grid.prototype.destroy = function(removeElements) {
	  if (this._isDestroyed) return this;
  
	  var container = this._element;
	  var items = this._items.slice(0);
	  var i;
  
	  // Unbind window resize event listener.
	  if (this._resizeHandler) {
		window.removeEventListener('resize', this._resizeHandler);
	  }
  
	  // Destroy items.
	  for (i = 0; i < items.length; i++) {
		items[i]._destroy(removeElements);
	  }
  
	  // Restore container.
	  removeClass(container, this._settings.containerClass);
	  container.style.height = '';
	  container.style.width = '';
  
	  // Emit destroy event and unbind all events.
	  this._emit(eventDestroy);
	  this._emitter.destroy();
  
	  // Remove reference from the grid instances collection.
	  gridInstances[this._id] = undefined;
  
	  // Flag instance as destroyed.
	  this._isDestroyed = true;
  
	  return this;
	};
  
	/**
	 * Private prototype methods
	 * *************************
	 */
  
	/**
	 * Get instance's item by element or by index. Target can also be an Item
	 * instance in which case the function returns the item if it exists within
	 * related Grid instance. If nothing is found with the provided target, null
	 * is returned.
	 *
	 * @private
	 * @memberof Grid.prototype
	 * @param {GridSingleItemQuery} [target]
	 * @returns {?Item}
	 */
	Grid.prototype._getItem = function(target) {
	  // If no target is specified or the instance is destroyed, return null.
	  if (this._isDestroyed || (!target && target !== 0)) {
		return null;
	  }
  
	  // If target is number return the item in that index. If the number is lower
	  // than zero look for the item starting from the end of the items array. For
	  // example -1 for the last item, -2 for the second last item, etc.
	  if (typeof target === 'number') {
		return this._items[target > -1 ? target : this._items.length + target] || null;
	  }
  
	  // If the target is an instance of Item return it if it is attached to this
	  // Grid instance, otherwise return null.
	  if (target instanceof Item) {
		return target._gridId === this._id ? target : null;
	  }
  
	  // In other cases let's assume that the target is an element, so let's try
	  // to find an item that matches the element and return it. If item is not
	  // found return null.
	  /** @todo This could be made a lot faster by using Map/WeakMap of elements. */
	  for (var i = 0; i < this._items.length; i++) {
		if (this._items[i]._element === target) {
		  return this._items[i];
		}
	  }
  
	  return null;
	};
  
	/**
	 * Recalculates and updates instance's layout data.
	 *
	 * @private
	 * @memberof Grid.prototype
	 * @returns {LayoutData}
	 */
	Grid.prototype._updateLayout = function() {
	  var layout = this._layout;
	  var settings = this._settings.layout;
	  var width;
	  var height;
	  var newLayout;
	  var i;
  
	  // Let's increment layout id.
	  ++layout.id;
  
	  // Let's update layout items
	  layout.items.length = 0;
	  for (i = 0; i < this._items.length; i++) {
		if (this._items[i]._isActive) layout.items.push(this._items[i]);
	  }
  
	  // Let's make sure we have the correct container dimensions.
	  this._refreshDimensions();
  
	  // Calculate container width and height (without borders).
	  width = this._width - this._borderLeft - this._borderRight;
	  height = this._height - this._borderTop - this._borderBottom;
  
	  // Calculate new layout.
	  if (typeof settings === 'function') {
		newLayout = settings(layout.items, width, height);
	  } else {
		newLayout = packer.getLayout(layout.items, width, height, layout.slots, settings);
	  }
  
	  // Let's update the grid's layout.
	  layout.slots = newLayout.slots;
	  layout.setWidth = Boolean(newLayout.setWidth);
	  layout.setHeight = Boolean(newLayout.setHeight);
	  layout.width = newLayout.width;
	  layout.height = newLayout.height;
  
	  return layout;
	};
  
	/**
	 * Emit a grid event.
	 *
	 * @private
	 * @memberof Grid.prototype
	 * @param {String} event
	 * @param {...*} [arg]
	 */
	Grid.prototype._emit = function() {
	  if (this._isDestroyed) return;
	  this._emitter.emit.apply(this._emitter, arguments);
	};
  
	/**
	 * Check if there are any events listeners for an event.
	 *
	 * @private
	 * @memberof Grid.prototype
	 * @param {String} event
	 * @returns {Boolean}
	 */
	Grid.prototype._hasListeners = function(event) {
	  var listeners = this._emitter._events[event];
	  return !!(listeners && listeners.length);
	};
  
	/**
	 * Update container's width, height and offsets.
	 *
	 * @private
	 * @memberof Grid.prototype
	 */
	Grid.prototype._updateBoundingRect = function() {
	  var element = this._element;
	  var rect = element.getBoundingClientRect();
	  this._width = rect.width;
	  this._height = rect.height;
	  this._left = rect.left;
	  this._top = rect.top;
	};
  
	/**
	 * Update container's border sizes.
	 *
	 * @private
	 * @memberof Grid.prototype
	 * @param {Boolean} left
	 * @param {Boolean} right
	 * @param {Boolean} top
	 * @param {Boolean} bottom
	 */
	Grid.prototype._updateBorders = function(left, right, top, bottom) {
	  var element = this._element;
	  if (left) this._borderLeft = getStyleAsFloat(element, 'border-left-width');
	  if (right) this._borderRight = getStyleAsFloat(element, 'border-right-width');
	  if (top) this._borderTop = getStyleAsFloat(element, 'border-top-width');
	  if (bottom) this._borderBottom = getStyleAsFloat(element, 'border-bottom-width');
	};
  
	/**
	 * Refresh all of container's internal dimensions and offsets.
	 *
	 * @private
	 * @memberof Grid.prototype
	 */
	Grid.prototype._refreshDimensions = function() {
	  this._updateBoundingRect();
	  this._updateBorders(1, 1, 1, 1);
	};
  
	/**
	 * Show or hide Grid instance's items.
	 *
	 * @private
	 * @memberof Grid.prototype
	 * @param {GridMultiItemQuery} items
	 * @param {Boolean} toVisible
	 * @param {Object} [options]
	 * @param {Boolean} [options.instant=false]
	 * @param {(ShowCallback|HideCallback)} [options.onFinish]
	 * @param {(Boolean|LayoutCallback|String)} [options.layout=true]
	 */
	Grid.prototype._setItemsVisibility = function(items, toVisible, options) {
	  var grid = this;
	  var targetItems = this.getItems(items);
	  var opts = options || 0;
	  var isInstant = opts.instant === true;
	  var callback = opts.onFinish;
	  var layout = opts.layout ? opts.layout : opts.layout === undefined;
	  var counter = targetItems.length;
	  var startEvent = toVisible ? eventShowStart : eventHideStart;
	  var endEvent = toVisible ? eventShowEnd : eventHideEnd;
	  var method = toVisible ? 'show' : 'hide';
	  var needsLayout = false;
	  var completedItems = [];
	  var hiddenItems = [];
	  var item;
	  var i;
  
	  // If there are no items call the callback, but don't emit any events.
	  if (!counter) {
		if (typeof callback === 'function') callback(targetItems);
		return;
	  }
  
	  // Emit showStart/hideStart event.
	  if (this._hasListeners(startEvent)) {
		this._emit(startEvent, targetItems.slice(0));
	  }
  
	  // Show/hide items.
	  for (i = 0; i < targetItems.length; i++) {
		item = targetItems[i];
  
		// If inactive item is shown or active item is hidden we need to do
		// layout.
		if ((toVisible && !item._isActive) || (!toVisible && item._isActive)) {
		  needsLayout = true;
		}
  
		// If inactive item is shown we also need to do a little hack to make the
		// item not animate it's next positioning (layout).
		if (toVisible && !item._isActive) {
		  item._layout._skipNextAnimation = true;
		}
  
		// If a hidden item is being shown we need to refresh the item's
		// dimensions.
		if (toVisible && item._visibility._isHidden) {
		  hiddenItems.push(item);
		}
  
		// Show/hide the item.
		item._visibility[method](isInstant, function(interrupted, item) {
		  // If the current item's animation was not interrupted add it to the
		  // completedItems array.
		  if (!interrupted) completedItems.push(item);
  
		  // If all items have finished their animations call the callback
		  // and emit showEnd/hideEnd event.
		  if (--counter < 1) {
			if (typeof callback === 'function') callback(completedItems.slice(0));
			if (grid._hasListeners(endEvent)) grid._emit(endEvent, completedItems.slice(0));
		  }
		});
	  }
  
	  // Refresh hidden items.
	  if (hiddenItems.length) this.refreshItems(hiddenItems);
  
	  // Layout if needed.
	  if (needsLayout && layout) {
		this.layout(layout === 'instant', typeof layout === 'function' ? layout : undefined);
	  }
	};
  
	/**
	 * Private helpers
	 * ***************
	 */
  
	/**
	 * Merge default settings with user settings. The returned object is a new
	 * object with merged values. The merging is a deep merge meaning that all
	 * objects and arrays within the provided settings objects will be also merged
	 * so that modifying the values of the settings object will have no effect on
	 * the returned object.
	 *
	 * @param {Object} defaultSettings
	 * @param {Object} [userSettings]
	 * @returns {Object} Returns a new object.
	 */
	function mergeSettings(defaultSettings, userSettings) {
	  // Create a fresh copy of default settings.
	  var ret = mergeObjects({}, defaultSettings);
  
	  // Merge user settings to default settings.
	  if (userSettings) {
		ret = mergeObjects(ret, userSettings);
	  }
  
	  // Handle visible/hidden styles manually so that the whole object is
	  // overridden instead of the props.
	  ret.visibleStyles = (userSettings || 0).visibleStyles || (defaultSettings || 0).visibleStyles;
	  ret.hiddenStyles = (userSettings || 0).hiddenStyles || (defaultSettings || 0).hiddenStyles;
  
	  return ret;
	}
  
	/**
	 * Merge two objects recursively (deep merge). The source object's properties
	 * are merged to the target object.
	 *
	 * @param {Object} target
	 *   - The target object.
	 * @param {Object} source
	 *   - The source object.
	 * @returns {Object} Returns the target object.
	 */
	function mergeObjects(target, source) {
	  var sourceKeys = Object.keys(source);
	  var length = sourceKeys.length;
	  var isSourceObject;
	  var propName;
	  var i;
  
	  for (i = 0; i < length; i++) {
		propName = sourceKeys[i];
		isSourceObject = isPlainObject(source[propName]);
  
		// If target and source values are both objects, merge the objects and
		// assign the merged value to the target property.
		if (isPlainObject(target[propName]) && isSourceObject) {
		  target[propName] = mergeObjects(mergeObjects({}, target[propName]), source[propName]);
		  continue;
		}
  
		// If source's value is object and target's is not let's clone the object as
		// the target's value.
		if (isSourceObject) {
		  target[propName] = mergeObjects({}, source[propName]);
		  continue;
		}
  
		// If source's value is an array let's clone the array as the target's
		// value.
		if (Array.isArray(source[propName])) {
		  target[propName] = source[propName].slice(0);
		  continue;
		}
  
		// In all other cases let's just directly assign the source's value as the
		// target's value.
		target[propName] = source[propName];
	  }
  
	  return target;
	}
  
	return Grid;
  
  })));